File Coverage

blib/lib/PDL/PP.pm
Criterion Covered Total %
statement 726 1022 71.0
branch 146 382 38.2
condition 28 105 26.6
subroutine 127 165 76.9
pod 17 88 19.3
total 1044 1762 59.2


line stmt bran cond sub pod time code
1             #####################################################################
2             #####################################################################
3             ##
4             ##
5             ## Here starts the actual thing.
6             ##
7             ## This is way too messy and uncommented. Still. :(
8             #
9             # DJB August 24 2006
10             # begin cleaning up the code so that it all runs under use strict
11             # DJB August 31 2006
12             # moved to use objects for the rule table (ie defvar) in the
13             # hope it's more declarative (since the addition of "::" to
14             # a statement makes it so much-more meaningful :-)
15             #
16              
17             # How to convert from the old deftbl format to the new, object-based,
18             # system:
19             #
20             # What used to be, in $PDL::PP::deftbl
21             # [["Name1"], ["Name2"], $ref_to_sub]]
22             # is now
23             # PDL::PP::Rule->new("Name1", "Name2", $ref_to_sub)
24             # where Name1 represents the target of the rule, Name2 the condition,
25             # and the subroutine reference is the routine called when the rule is
26             # applied.
27             #
28             # If there is no condition, the argument can be left out of the call
29             # (unless there is a doc string), so
30             # [["Name1"], [], $ref_to_sub]]
31             # becomes
32             # PDL::PP::Rule->new("Name1", $ref_to_sub)
33             #
34             # The target and conditions can also be an array reference, so
35             # [["Name1"], ["Name2","Name3"], $ref_to_sub]]
36             # [["Name1","Name2"], ["Name3"], $ref_to_sub]]
37             # [["Name1","Name2"], ["Name3","Name4"], $ref_to_sub]]
38             # become, respectively
39             # PDL::PP::Rule->new("Name1", ["Name2","Name3"], $ref_to_sub)
40             # PDL::PP::Rule->new(["Name1","Name2"], "Name3", $ref_to_sub)
41             # PDL::PP::Rule->new(["Name1","Name2"], ["Name3","Name4], $ref_to_sub)
42             #
43             # If the old rule had a document string, this is placed between
44             # the condition and the subroutine reference. To make processing
45             # simpler, if a doc string exists then the condition must also
46             # be supplied, even if it is just [] (ie no condition).
47             #
48             # There are specialized rules for common situations. The rules for the
49             # target, condition, and doc arguments hold from the base class (ie
50             # whether scalar or array values are used, ...)
51             #
52             # Return a constant:
53             #
54             # PDL::PP::Rule::Returns->new($targets [,$conditions [,$doc]], $value)
55             # is used to return a constant. So
56             # [["Name1"], [], sub { "foo" }]
57             # becomes
58             # PDL::PP::Rule::Returns->new("Name1", "foo")
59             #
60             # This class is specialized since there are some common return values:
61             # PDL::PP::Rule::Returns::Zero->new($targets [,$conditions [,$doc]])
62             # PDL::PP::Rule::Returns::One->new($targets [,$conditions [,$doc]])
63             # PDL::PP::Rule::Returns::EmptyString->new($targets [,$conditions [,$doc]])
64             # PDL::PP::Rule::Returns::NULL->new($targets [,$conditions [,$doc]])
65             # which return 0, 1, "", and "NULL" respectively
66             #
67             # The InsertName class exists to allow you to return something like
68             # "foobar"
69             # The old rules
70             # [["Foo"], ["Name"], sub { return "_pdl_$_[0]_bar"; }]
71             # [["Foo"], ["Name","Arg2"], sub { return "_pdl_$_[0]_bar"; }]
72             # become
73             # PDL::PP::Rule::InsertName->new("Foo", '_pdl_${name}_bar')
74             # PDL::PP::Rule::InsertName->new("Foo", "Arg2", '_pdl_${name}_bar')
75             # Note that the Name argument is automatically used as a condition, so
76             # it does not need to be supplied, and the return value should be
77             # given as a single-quoted string and use the $name variable
78             #
79             # The Substitute rule replaces dollar-signed macros ($P(), $ISBAD(), etc)
80             # with the low-level C code to perform the macro.
81             #
82             # The Substitute class replaces the dosubst rule. The old rule
83             # [["NewXSCoerceMustSubs"], ["NewXSCoerceMustSub1","NewXSSymTab","Name"],
84             # \&dosubst]
85             # becomes
86             # PDL::PP::Rule::Substitute("NewXSCoerceMustSubs", "NewXSCoerceMustSub1")
87             #
88             # PDL::PP::Rule::Substitute->new($target,$condition)
89             # $target and $condition must be scalars.
90             #
91             # Implicit conditions are NewXSSymTab and Name
92             #
93             # The Substitute:Usual class replaces the dousualsubsts rule. The old rule
94             # [["CacheBadFlagInit"], ["CacheBadFlagInitNS","NewXSSymTab","Name"],
95             # \&dousualsubsts],
96             # becomes
97             # PDL::PP::Rule::Substitute::Usual->new("CacheBadFlagInit", "CacheBadFlagInitNS")
98             #
99             # PDL::PP::Rule::Substitute::Usual->new($target, $condition)
100             # $target and $condition must be scalars.
101             #
102             # Implicit conditions are NewXSSymTab and Name
103             #
104             # The MakeComp rule replaces the subst_makecomp routine. The old rule
105             # [["MakeCompiledRepr"], ["MakeComp","CompNames","CompObjs"],
106             # sub {subst_makecomp("COMP",@_)}]
107             # becomes
108             # PDL::PP::Rule::MakeComp->new("MakeCompiledRepr", ["MakeComp","CompNames","CompObjs"],
109             # "COMP")
110             # PDL::PP::Rule::MakeComp->new($target,$conditions,$symbol)
111             # $target and $symbol must be scalars.
112             #
113              
114             # Notes:
115             # InsertName could become a subclass of Insert since there are
116             # a few rules that just insert conditions into a text string.
117             #
118             # Substitute, Substitute::Usual, MakeComp classes feel a bit
119             # ugly. See next point. Also the get_std_childparent method is
120             # a bit of a hack.
121             #
122             # DJB thinks that the code fragments themselves could be objects
123             # since they should 'know' what needs doing to them (eg the
124             # substitutions). Not sure whether it would really clarify things.
125             #
126             # To do:
127             # wrap_vfn could probably be moved into a class.
128             #
129             # move the PDL::PP::Rule and subclasses into their own file?
130             #
131              
132             package PDL::PP::Rule;
133              
134 2     2   148482 use strict;
  2         24  
  2         85  
135             require PDL::Core::Dev;
136              
137 2     2   11 use Carp;
  2         4  
  2         381  
138             our @CARP_NOT;
139              
140             my $INVALID_OTHERPARS_RE = qr/^(?:magicno|flags|vtable|freeproc|bvalflag|has_badvalue|badvalue|pdls|__datatype)\z/;
141              
142 2     2   2582 use overload ("\"\"" => \&PDL::PP::Rule::stringify);
  2         2259  
  2         18  
143             sub stringify {
144 205     205   277 my $self = shift;
145              
146 205         302 my $str = ref $self;
147 205 100       366 if ("PDL::PP::Rule" eq $str) {
148 120         158 $str = "Rule";
149             } else {
150 85         283 $str =~ s/PDL::PP::Rule:://;
151             }
152 205         376 $str = "($str) ";
153             $str .= exists $self->{doc} ?
154 205 100       382 $self->{doc} : join(",", @{$self->{targets}});
  145         330  
155 205         658 return $str;
156             }
157              
158             # Takes two args: the calling object and the message, but we only care
159             # about the message:
160 619 50   619   1160 sub report ($$) { print $_[1] if $::PP_VERBOSE; }
161              
162             # Very limited error checking.
163             # Allow scalars for targets and conditions to be optional
164             #
165             # At present you have to have a conditions argument if you supply
166             # a doc string
167             #
168             # It seems strange to make the subroutine reference an optional
169             # argument but this is being used to transition to a slightly-different
170             # object design
171             #
172             sub new {
173 270     270   389 my $class = shift;
174              
175 270         400 my $self = {};
176 270         391 bless $self, $class;
177              
178 270         331 my $usage = "Usage: PDL::PP::Rule->new(\$targets[,\$conditions[,\$doc],] [,\$ref])\n";
179              
180             # handle arguments
181 270         356 my $nargs = $#_;
182 270 50 33     747 die $usage if $nargs < 0 or $nargs > 3;
183              
184 270         375 my $targets = shift;
185 270 100       572 $targets = [$targets] unless ref $targets eq "ARRAY";
186 270         1202 $self->{targets} = $targets;
187              
188 270 100       618 if ($#_ != -1) {
189 236 100       408 if (ref $_[-1] eq "CODE") {
190 200         312 $self->{ref} = pop;
191             }
192              
193 236         346 my ($conditions,$doc) = @_;
194              
195 236 100       373 if (defined $conditions) {
196 234 100       441 $conditions = [$conditions] unless ref $conditions eq "ARRAY";
197             } else {
198 2         4 $conditions = [];
199             }
200 236         380 $self->{conditions} = $conditions;
201 236 100       421 $self->{doc} = $doc if defined $doc;
202             }
203              
204 270         1701 return $self;
205             }
206              
207             # $rule->check_if_targets_exist($pars);
208             #
209             # Returns 1 if any of the targets exist in $pars, 0 otherwise.
210             # A return value of 1 means that the rule should not be applied.
211             #
212             # Not 100% happy with use of report here. Needs re-thinking.
213             #
214             sub check_if_targets_exist {
215 209     209   261 my $self = shift;
216 209         243 my $pars = shift;
217              
218 209         358 my $targets = $self->{targets};
219              
220 209         310 foreach my $target (@$targets) {
221 226 100       466 if (exists $pars->{$target}) {
222 8         26 $self->report("--skipping since TARGET $target exists\n");
223 8         31 return 1;
224             }
225             }
226 201         364 return 0;
227             }
228              
229             # $rule->check_if_conditions_exist($pars);
230             #
231             # Returns 1 if all of the required conditions exist in $pars, 0 otherwise.
232             # A return value of 0 means that the rule should not be applied.
233             #
234             # Not 100% happy with use of report here. Needs re-thinking.
235             #
236             sub check_if_conditions_exist {
237 201     201   256 my $self = shift;
238 201         245 my $pars = shift;
239              
240 201         262 my $conditions = $self->{conditions};
241              
242 201         309 foreach my $condition (@$conditions) {
243              
244             # skip if not a required condition
245 405 100       742 next if substr($condition,0,1) eq "_";
246              
247 365 100       673 unless (exists $pars->{$condition}) {
248 61         166 $self->report("--skipping since CONDITION $condition does not exist\n");
249 61         218 return 0;
250             }
251             }
252              
253 140         259 return 1;
254             }
255              
256             # $rule->is_valid($pars);
257             #
258             # Returns 1 if the rule should be applied (ie no targets already
259             # exist in $pars and all the required conditions exist in $pars),
260             # otherwise 0.
261             #
262             sub is_valid {
263 209     209   276 my $self = shift;
264 209         254 my $pars = shift;
265              
266 209 100       373 return 0 if $self->check_if_targets_exist($pars);
267 201 100       423 return 0 unless $self->check_if_conditions_exist($pars);
268 140         265 return 1;
269             }
270              
271             # my @args = $self->extract_args($pars);
272             #
273             # If this method is called we assume that
274             # $self->check_if_conditions_exist($pars)
275             # returns 1.
276             #
277             sub extract_args {
278 83     83   110 my $self = shift;
279 83         100 my $pars = shift;
280              
281 83         137 my $conditions = $self->{conditions};
282              
283 83         109 my @args;
284 83         125 foreach (@$conditions) {
285             # make a copy of each condition so that any changes to it are not
286             # also made to the original array!
287 293         349 my $condition = $_;
288             # Remove any possible underscores (which indicate optional conditions):
289 293         460 $condition =~ s/^_//;
290              
291             # Note: This will *not* create $pars->{$condition} if it did not already
292             # exist:
293 293         511 push @args, $pars->{$condition};
294             }
295              
296 83         221 return @args;
297             }
298              
299             # Apply the rule using the supplied $pars hash reference.
300             #
301             sub apply {
302 146     146   188 my $self = shift;
303 146         181 my $pars = shift;
304              
305             carp "Unable to apply rule $self as there is no subroutine reference!"
306 146 50       477 unless exists $self->{ref};
307              
308 146         205 my $targets = $self->{targets};
309 146         227 my $conditions = $self->{conditions};
310 146         187 my $ref = $self->{ref};
311              
312 146         304 $self->report("Applying: $self\n");
313              
314             # Is the rule valid?
315             #
316 146 100       312 return unless $self->is_valid($pars);
317              
318             # Create the argument array for the routine.
319             #
320 98         188 my @args = $self->extract_args($pars);
321              
322             # Run this rule's subroutine:
323 98         304 my @retval = $self->{ref}(@args);
324              
325             # Check for any inconsistencies:
326 97 50       1368 confess "Internal error: rule '$self' returned " . (1+$#retval)
327             . " items and expected " . (1+$#$targets)
328             unless $#retval == $#$targets;
329              
330 97         266 $self->report("--setting:");
331 97         158 foreach my $target (@$targets) {
332 109         286 $self->report(" $target");
333             confess "Cannot have multiple meanings for target $target!"
334 109 50       241 if exists $pars->{$target};
335 109         170 my $result = shift @retval;
336              
337             # The following test suggests that things could/should be
338             # improved in the code generation.
339             #
340 109 50 66     371 if (defined $result and $result eq 'DO NOT SET!!') {
341 0         0 $self->report (" is 'DO NOT SET!!'");
342             } else {
343 109         295 $pars->{$target} = $result;
344             }
345             }
346 97         177 $self->report("\n");
347             }
348              
349              
350             package PDL::PP::Rule::Croak;
351              
352             # Croaks if all of the input variables are defined. Use this to identify
353             # incompatible arguments.
354             our @ISA = qw(PDL::PP::Rule);
355 2     2   2146 use Carp;
  2         5  
  2         450  
356             our @CARP_NOT;
357              
358              
359             sub new {
360 4 50   4   17 croak('Usage: PDL::PP::Ruel::Croak->new(["incompatible", "arguments"], "Croaking message")')
361             unless @_ == 3;
362            
363 4         7 my $class = shift;
364 4         21 my $self = $class->SUPER::new([], @_);
365 4         46 return bless $self, $class;
366             }
367              
368             sub apply {
369 4     4   8 my ($self, $pars) = @_;
370 4 50       24 croak($self->{doc}) if $self->is_valid($pars);
371             }
372              
373             package PDL::PP::Rule::Returns;
374              
375 2     2   17 use strict;
  2         3  
  2         50  
376              
377 2     2   19 use Carp;
  2         5  
  2         676  
378             our @CARP_NOT;
379              
380             ##use PDL::PP::Rule;
381             our @ISA = qw (PDL::PP::Rule);
382              
383             # This class does not treat return values of "DO NOT SET!!"
384             # as special.
385             #
386             sub new {
387 50     50   70 my $class = shift;
388              
389 50         65 my $value = pop;
390              
391 50         98 my @args = @_;
392 50         90 my $self = $class->SUPER::new(@args);
393 50         75 bless $self, $class;
394 50         76 $self->{"returns.value"} = $value;
395              
396 50         68 my $targets = $self->{targets};
397 50 50       88 croak "There can only be 1 target for a $self, not " . (1+$#$targets) . "!"
398             unless $#$targets == 0;
399              
400 50         183 return $self;
401             }
402              
403             sub apply {
404 44     44   67 my $self = shift;
405 44         57 my $pars = shift;
406              
407             carp "Unable to apply rule $self as there is no return value!"
408 44 50       432 unless exists $self->{"returns.value"};
409              
410 44         84 my $target = $self->{targets}->[0];
411              
412 44         94 $self->report("Applying: $self\n");
413              
414             # Is the rule valid?
415             #
416 44 100       112 return unless $self->is_valid($pars);
417              
418             # Set the value
419             #
420 28         78 $self->report ("--setting: $target\n");
421 28         147 $pars->{$target} = $self->{"returns.value"};
422             }
423              
424             package PDL::PP::Rule::Returns::Zero;
425              
426 2     2   15 use strict;
  2         4  
  2         231  
427              
428             ##use PDL::PP::Rule::Returns;
429             our @ISA = qw (PDL::PP::Rule::Returns);
430              
431             sub new {
432 6     6   8 my $class = shift;
433 6         14 my @args = @_;
434 6         20 my $self = $class->SUPER::new(@args,0);
435 6         199 bless $self, $class;
436 6         37 return $self;
437             }
438              
439             package PDL::PP::Rule::Returns::One;
440              
441 2     2   14 use strict;
  2         4  
  2         194  
442              
443             ##use PDL::PP::Rule::Returns;
444             our @ISA = qw (PDL::PP::Rule::Returns);
445              
446             sub new {
447 4     4   7 my $class = shift;
448 4         10 my @args = @_;
449 4         17 my $self = $class->SUPER::new(@args,1);
450 4         7 bless $self, $class;
451 4         21 return $self;
452             }
453              
454             package PDL::PP::Rule::Returns::EmptyString;
455              
456 2     2   13 use strict;
  2         4  
  2         232  
457              
458             ##use PDL::PP::Rule::Returns;
459             our @ISA = qw (PDL::PP::Rule::Returns);
460              
461             sub new {
462 12     12   20 my $class = shift;
463 12         25 my @args = @_;
464 12         50 my $self = $class->SUPER::new(@args,"");
465 12         70 bless $self, $class;
466 12         132 return $self;
467             }
468              
469             package PDL::PP::Rule::Returns::NULL;
470              
471 2     2   14 use strict;
  2         20  
  2         221  
472              
473             ##use PDL::PP::Rule::Returns;
474             our @ISA = qw (PDL::PP::Rule::Returns);
475              
476             sub new {
477 8     8   12 my $class = shift;
478 8         17 my @args = @_;
479 8         21 my $self = $class->SUPER::new(@args,"NULL");
480 8         12 bless $self, $class;
481 8         48 return $self;
482             }
483              
484             package PDL::PP::Rule::InsertName;
485              
486 2     2   13 use strict;
  2         3  
  2         63  
487              
488 2     2   11 use Carp;
  2         3  
  2         775  
489             our @CARP_NOT;
490              
491             ##use PDL::PP::Rule;
492             our @ISA = qw (PDL::PP::Rule);
493              
494             # This class does not treat return values of "DO NOT SET!!"
495             # as special.
496             #
497             sub new {
498 16     16   26 my $class = shift;
499              
500 16         25 my $value = pop;
501              
502 16         28 my @args = @_;
503 16         42 my $self = $class->SUPER::new(@args);
504 16         26 bless $self, $class;
505 16         28 $self->{"insertname.value"} = $value;
506              
507             # Generate a defaul doc string
508 16 50       29 unless (exists $self->{doc}) {
509 16         54 $self->{doc} = 'Sets ' . $self->{targets}->[0]
510             . ' to "' . $value . '"';
511             }
512              
513 16         28 my $targets = $self->{targets};
514 16 50       36 croak "There can only be 1 target for a $self, not " . (1+$#$targets) . "!"
515             unless $#$targets == 0;
516              
517             # we add "Name" as the first condition
518             #
519 16         39 my $conditions = $self->{conditions};
520 16         72 unshift @$conditions, "Name";
521              
522 16         95 return $self;
523             }
524              
525             sub apply {
526 15     15   26 my $self = shift;
527 15         23 my $pars = shift;
528              
529             carp "Unable to apply rule $self as there is no return value!"
530 15 50       92 unless exists $self->{"insertname.value"};
531              
532 15         38 $self->report("Applying: $self\n");
533              
534             # Is the rule valid?
535             #
536 15 100       34 return unless $self->is_valid($pars);
537              
538             # Set the value
539             #
540 14         26 my $target = $self->{targets}->[0];
541 14         21 my $name = $pars->{Name};
542 14         53 $self->report ("--setting: $target (name=$name)\n");
543 14         769 $pars->{$target} = eval "return \"" . $self->{"insertname.value"} . "\";";
544             }
545              
546             # Poor name. This is the old "dosubst" routine
547             #
548             # PDL::PP::Rule->new("NewXSCoerceMustSubs", ["NewXSCoerceMustSub1","NewXSSymTab","Name"],
549             # \&dosubst),
550             #
551             # PDL::PP::Rule::Substitute->new($target,$condition)
552             # $target and $condition must be scalars.
553             #
554             # Implicit conditions are NewXSSymTab and Name
555             #
556             package PDL::PP::Rule::Substitute;
557              
558 2     2   15 use strict;
  2         3  
  2         50  
559              
560 2     2   9 use Carp;
  2         3  
  2         1696  
561             our @CARP_NOT;
562              
563             ##use PDL::PP::Rule;
564             our @ISA = qw (PDL::PP::Rule);
565              
566             # Probably want this directly in the apply routine but leave as is for now
567             #
568             sub dosubst_private {
569 16     16   35 my ($src,$symtab,$name) = @_;
570 16 100       38 my $ret = (ref $src ? $src->[0] : $src);
571             my %syms = (
572 15         364 ((ref $src) ? %{$src->[1]} : ()),
573 101     101   178 PRIV => sub {return "".$symtab->get_symname('_PDL_ThisTrans').
574             "->$_[0]"},
575 1     1   5 CROAK => sub {PDL::PP::pp_line_numbers(__LINE__, "PDL->pdl_barf(\"Error in $name:\" $_[0])")},
576 1     1   7 NAME => sub {return $name},
577 1     1   4 MODULE => sub {return $::PDLMOD},
578              
579 0     0   0 SETPDLSTATEBAD => sub { PDL::PP::pp_line_numbers(__LINE__, "$_[0]\->state |= PDL_BADVAL") },
580 0     0   0 SETPDLSTATEGOOD => sub { PDL::PP::pp_line_numbers(__LINE__, "$_[0]\->state &= ~PDL_BADVAL") },
581 1     1   5 ISPDLSTATEBAD => sub { PDL::PP::pp_line_numbers(__LINE__, "(($_[0]\->state & PDL_BADVAL) > 0)") },
582 0     0   0 ISPDLSTATEGOOD => sub { PDL::PP::pp_line_numbers(__LINE__, "(($_[0]\->state & PDL_BADVAL) == 0)") },
583 5     5   12 BADFLAGCACHE => sub { PDL::PP::pp_line_numbers(__LINE__, "badflag_cache") },
584              
585             SETREVERSIBLE => sub {
586 0     0   0 PDL::PP::pp_line_numbers(__LINE__, "if($_[0]) \$PRIV(flags) |= PDL_ITRANS_REVERSIBLE;\n" .
587             " else \$PRIV(flags) &= ~PDL_ITRANS_REVERSIBLE;\n")
588             },
589 16 100       46 );
590 16         121 while(
591             $ret =~ s/\$(\w+)\(([^()]*)\)/
592             (defined $syms{$1} or
593             confess("$1 not defined in '$ret'!")) and
594 110 50 33     342 (&{$syms{$1}}($2))/ge
  110         194  
595             ) {};
596 16         216 $ret;
597             }
598              
599             sub new {
600 32     32   95 my $class = shift;
601              
602 32 50       64 die "Usage: PDL::PP::Rule::Substitute->new(\$target,\$condition);"
603             unless $#_ == 1;
604              
605 32         42 my $target = shift;
606 32         43 my $condition = shift;
607              
608 32 50       54 die "\$target must be a scalar for PDL::PP::Rule->Substitute" if ref $target;
609 32 50       47 die "\$condition must be a scalar for PDL::PP::Rule->Substitute" if ref $condition;
610              
611 32         108 my $self = $class->SUPER::new($target, [$condition, "NewXSSymTab", "Name"],
612             \&dosubst_private);
613 32         47 bless $self, $class;
614              
615 32         148 return $self;
616             }
617              
618             # Poor name. This is the old "dousualsubsts" routine
619             #
620             # PDL::PP::Rule->new("CacheBadFlagInit", ["CacheBadFlagInitNS","NewXSSymTab","Name"],
621             # \&dousualsubsts),
622             #
623             # PDL::PP::Rule::Substitute::Usual->new($target, $condition)
624             # $target and $condition must be scalars.
625             #
626             # Implicit conditions are NewXSSymTab and Name
627             #
628             # Need to think about @std_childparent as it is also used by
629             # other bits of code. At the moment provide a class method
630             # to access the array but there has to be better ways of
631             # doing this.
632             #
633             package PDL::PP::Rule::Substitute::Usual;
634              
635 2     2   16 use strict;
  2         5  
  2         79  
636              
637 2     2   12 use Carp;
  2         4  
  2         908  
638             our @CARP_NOT;
639              
640             ##use PDL::PP::Rule;
641             our @ISA = qw (PDL::PP::Rule::Substitute);
642              
643             # This is a copy of the main one for now. Need a better solution.
644             #
645             my @std_childparent = (
646             CHILD => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV(pdls[1]->'.(join ',',@_).")")},
647             PARENT => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV(pdls[0]->'.(join ',',@_).")")},
648             CHILD_P => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV(pdls[1]->'.(join ',',@_).")")},
649             PARENT_P => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV(pdls[0]->'.(join ',',@_).")")},
650             CHILD_PTR => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV(pdls[1])')},
651             PARENT_PTR => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV(pdls[0])')},
652             COMP => sub {PDL::PP::pp_line_numbers(__LINE__, '$PRIV('.(join ',',@_).")")}
653             );
654              
655 3     3   30 sub get_std_childparent { return @std_childparent; }
656              
657             sub new {
658 22     22   34 my $class = shift;
659              
660 22         40 my @args = @_;
661 22         50 my $self = $class->SUPER::new(@args);
662 22         29 bless $self, $class;
663              
664 22         105 return $self;
665             }
666              
667             # We modify the arguments from the conditions to include the
668             # extra information
669             #
670             # We simplify the base-class version since we assume that all
671             # conditions are required here.
672             #
673             sub extract_args {
674 12     12   16 my $self = shift;
675 12         17 my $pars = shift;
676              
677             # The conditions are [, NewXSSymTab, Name]
678             #
679 12         24 my $code = $pars->{$self->{conditions}[0]};
680 12         23 my $symtab = $pars->{$self->{conditions}[1]};
681 12         19 my $name = $pars->{$self->{conditions}[2]};
682              
683 12         84 return ([$code,{@std_childparent}],$symtab,$name);
684             }
685              
686             # Poor name. This is the old "subst_makecomp" routine
687             #
688             # PDL::PP::Rule->new("MakeCompiledRepr", ["MakeComp","CompNames","CompObjs"],
689             # sub {subst_makecomp("COMP",@_)}),
690             #
691             # PDL::PP::Rule::MakeComp->new($target,$conditions,$symbol)
692             # $target and $symbol must be scalars.
693             #
694             package PDL::PP::Rule::MakeComp;
695              
696 2     2   14 use strict;
  2         4  
  2         49  
697              
698 2     2   10 use Carp;
  2         3  
  2         1126  
699             our @CARP_NOT;
700              
701             ##use PDL::PP::Rule;
702             our @ISA = qw (PDL::PP::Rule);
703              
704             # This is a copy of the main one for now. Need a better solution.
705             #
706             my @std_redodims = (
707             SETNDIMS => sub {PDL::PP::pp_line_numbers(__LINE__, "PDL->reallocdims(__it,$_[0])")},
708             SETDIMS => sub {PDL::PP::pp_line_numbers(__LINE__, "PDL->setdims_careful(__it)")},
709             SETDELTATHREADIDS => sub {PDL::PP::pp_line_numbers(__LINE__, '
710             {int __ind; PDL->reallocthreadids($CHILD_PTR(),
711             $PARENT(nthreadids));
712             for(__ind=0; __ind<$PARENT(nthreadids)+1; __ind++) {
713             $CHILD(threadids[__ind]) =
714             $PARENT(threadids[__ind]) + ('.$_[0].');
715             }
716             }
717             ')});
718              
719             ##sub get_std_redodims { return @std_redodims; }
720              
721             # Probably want this directly in the apply routine but leave as is for now
722             #
723             sub subst_makecomp_private {
724 3     3   8 my($which,$mc,$cn,$co) = @_;
725             return [$mc,{
726             # @::std_childparent,
727             PDL::PP::Rule::Substitute::Usual::get_std_childparent(),
728             ($cn ?
729             (('DO'.$which.'DIMS') => sub {PDL::PP::pp_line_numbers(__LINE__, join '',
730 0     0   0 map{$$co{$_}->need_malloc ?
731 0 0       0 $$co{$_}->get_malloc('$PRIV('.$_.')') :
732 3 100       8 ()} @$cn)}) :
    100          
733             ()
734             ),
735             ($which eq "PRIV" ?
736             @std_redodims : ()),
737             },
738             ];
739             }
740              
741             sub new {
742 10     10   23 my $class = shift;
743              
744 10 50       23 die "Usage: PDL::PP::Rule::MakeComp->new(\$target,\$conditions,\$symbol);"
745             unless $#_ == 2;
746              
747 10         16 my $target = shift;
748 10         13 my $condition = shift;
749 10         14 my $symbol = shift;
750              
751 10 50       38 die "\$target must be a scalar for PDL::PP::Rule->MakeComp" if ref $target;
752 10 50       21 die "\$symbol must be a scalar for PDL::PP::Rule->MakeComp" if ref $symbol;
753              
754 10         41 my $self = $class->SUPER::new($target, $condition,
755             \&subst_makecomp_private);
756 10         19 bless $self, $class;
757 10         23 $self->{"makecomp.value"} = $symbol;
758              
759 10         46 return $self;
760             }
761              
762             # We modify the arguments from the conditions to include the
763             # extra information
764             #
765             # We simplify the base-class version since we assume that all
766             # conditions are required here.
767             #
768             sub extract_args {
769 3     3   5 my $self = shift;
770 3         5 my $pars = shift;
771              
772             # The conditions are [, conditions...]
773             # - could use slicing here
774             #
775 3         7 my @args = ($self->{"makecomp.value"});
776 3         4 foreach my $condition (@{$self->{conditions}}) {
  3         8  
777 5         9 push @args, $pars->{$condition};
778             }
779 3         7 return @args;
780             }
781              
782             package PDL::PP;
783              
784 2     2   17 use strict;
  2         12  
  2         116  
785              
786             our $VERSION = "2.3";
787             $VERSION = eval $VERSION;
788              
789 2     2   1238 use PDL::Types ':All';
  2         5  
  2         531  
790 2     2   16 use Config;
  2         3  
  2         77  
791 2     2   1142 use FileHandle;
  2         21503  
  2         12  
792 2     2   654 use Exporter;
  2         5  
  2         92  
793              
794 2     2   1516 use Data::Dumper;
  2         14895  
  2         362  
795              
796             our @ISA = qw(Exporter);
797              
798             @PDL::PP::EXPORT = qw/pp_addhdr pp_addpm pp_bless pp_def pp_done pp_add_boot
799             pp_add_exported pp_addxs pp_add_isa pp_export_nothing
800             pp_core_importList pp_beginwrap pp_setversion
801             pp_addbegin pp_boundscheck pp_line_numbers
802             pp_deprecate_module/;
803              
804             $PP::boundscheck = 1;
805             $::PP_VERBOSE = 0;
806              
807             $PDL::PP::done = 0; # pp_done has not been called yet
808              
809       2     END {
810             #you can uncomment this for testing, but this should remain
811             #commented in production code. This causes pp_done to be called
812             #even when a .pd file aborts with die(), potentially bypassing
813             #problem code when build is re-attempted. Having this commented
814             #means we are a bit more strict: a module must call pp_done in
815             #order to have .xs and .pm files written.
816             # pp_done() unless $PDL::PP::done;
817             }
818              
819 2     2   18 use Carp;
  2         4  
  2         134  
820             our @CARP_NOT;
821              
822             # check for bad value support
823 2     2   993 use PDL::Config;
  2         5  
  2         4841  
824             my $bvalflag = $PDL::Config{WITH_BADVAL} || 0;
825              
826             my $ntypes = $#PDL::Types::names;
827              
828 2     2 0 8 sub nopm { $::PDLPACK eq 'NONE' } # flag that we don't want to generate a PM
829              
830             sub import {
831 2     2   49 my ($mod,$modname, $packname, $prefix, $callpack) = @_;
832             # Allow for users to not specify the packname
833 2 50       15 ($packname, $prefix, $callpack) = ($modname, $packname, $prefix)
834             if ($packname =~ m|/|);
835              
836 2         6 $::PDLMOD=$modname; $::PDLPACK=$packname; $::PDLPREF=$prefix;
  2         4  
  2         5  
837 2 50       10 $::CALLPACK = defined $callpack ? $callpack : $::PDLMOD;
838 2         5 $::PDLOBJ = "PDL"; # define pp-funcs in this package
839 2         6 $::PDLXS="";
840 2         4 $::PDLBEGIN="";
841 2         6 $::PDLPMROUT="";
842 2         7 for ('Top','Bot','Middle') { $::PDLPM{$_}="" }
  6         17  
843 2         6 @::PDLPMISA=('PDL::Exporter', 'DynaLoader');
844 2         7 @::PDL_IFBEGINWRAP = ('','');
845 2         5 $::PDLVERSIONSET = '';
846 2         3 $::PDLMODVERSION = undef;
847 2         5 $::DOCUMENTED = 0;
848 2         5 $::PDLCOREIMPORT = ""; #import list from core, defaults to everything, i.e. use Core
849             # could be set to () for importing nothing from core. or qw/ barf / for
850             # importing barf only.
851 2         4 @_=("PDL::PP");
852 2         220150 goto &Exporter::import;
853             }
854              
855              
856             # query/set boundschecking
857             # if on the generated XS code will have optional boundschecking
858             # that can be turned on/off at runtime(!) using
859             # __PACKAGE__::set_boundscheck(arg); # arg should be 0/1
860             # if off code is speed optimized and no runtime boundschecking
861             # can be performed
862             # ON by default
863             sub pp_boundscheck {
864 0     0 1 0 my $ret = $PP::boundscheck;
865 0 0       0 $PP::boundscheck = $_[0] if $#_ > -1;
866 0         0 return $ret;
867             }
868              
869             sub pp_beginwrap {
870 0     0 1 0 @::PDL_IFBEGINWRAP = ('BEGIN {','}');
871             }
872              
873             sub pp_setversion {
874 0     0 1 0 my ($ver) = @_;
875 0         0 $::PDLMODVERSION = '$VERSION';
876 0         0 $::PDLVERSIONSET = "\$$::PDLPACK\::VERSION = $ver;";
877             }
878              
879             sub pp_addhdr {
880 0     0 1 0 my ($hdr) = @_;
881 0         0 $::PDLXSC .= $hdr;
882             }
883              
884             sub pp_addpm {
885 3     3 1 5 my $pm = shift;
886 3         4 my $pos;
887 3 50       6 if (ref $pm) {
888 0         0 my $opt = $pm;
889 0         0 $pm = shift;
890             croak "unknown option" unless defined $opt->{At} &&
891 0 0 0     0 $opt->{At} =~ /^(Top|Bot|Middle)$/;
892 0         0 $pos = $opt->{At};
893             } else {
894 3         4 $pos = 'Middle';
895             }
896 3         8 $::PDLPM{$pos} .= "$pm\n\n";
897             }
898              
899             sub pp_add_exported {
900             # my ($this,$exp) = @_;
901 1     1 1 2 my $exp = join ' ', @_; # get rid of this silly $this argument
902 1         4 $::PDLPMROUT .= $exp." ";
903             }
904              
905             sub pp_addbegin {
906 0     0 1 0 my ($cmd) = @_;
907 0 0       0 if ($cmd =~ /^\s*BOOT\s*$/) {
908 0         0 pp_beginwrap;
909             } else {
910 0         0 $::PDLBEGIN .= $cmd."\n";
911             }
912             }
913              
914             # Sub to call to export nothing (i.e. for building OO package/object)
915             sub pp_export_nothing {
916 0     0 1 0 $::PDLPMROUT = ' ';
917             }
918              
919             sub pp_add_isa {
920 0     0 1 0 push @::PDLPMISA,@_;
921             }
922              
923             sub pp_add_boot {
924 1     1 1 2 my ($boot) = @_;
925 1         2 $::PDLXSBOOT .= $boot." ";
926             }
927              
928             sub pp_bless{
929 0     0 1 0 my($new_package)=@_;
930 0         0 $::PDLOBJ = $new_package;
931             }
932              
933             # sub to call to set the import list from core on the 'Use Core' line in the .pm file.
934             # set to '()' to not import anything from Core, or 'qw/ barf /' to import barf.
935             sub pp_core_importList{
936 0     0 1 0 $::PDLCOREIMPORT = $_[0];
937             }
938              
939             sub printxs {
940 1     1 0 2 shift;
941 1         5 $::PDLXS .= join'',@_;
942             }
943              
944             sub pp_addxs {
945 0     0 1 0 PDL::PP->printxs("\nMODULE = $::PDLMOD PACKAGE = $::CALLPACK\n\n",
946             @_,
947             "\nMODULE = $::PDLMOD PACKAGE = $::PDLOBJ\n\n");
948             }
949              
950             # inserts #line directives into source text. Use like this:
951             # ...
952             # FirstKey => ...,
953             # Code => pp_line_numbers(__LINE__, $x . $y . $c),
954             # OtherKey => ...
955              
956             sub pp_line_numbers ($$) {
957 75     75 1 256 my ($line, $string) = @_;
958             # The line needs to be incremented by one for the bookkeeping to work
959 75         112 $line++;
960             # Get the source filename using caller()
961 75         205 my (undef, $filename) = caller;
962             # Escape backslashes:
963 75         153 $filename =~ s/\\/\\\\/g;
964 75         176 my @to_return = "\n#line $line \"$filename\"\n";
965              
966             # Look for threadloops and loops and add # line directives
967 75         437 foreach (split (/\n/, $string)) {
968             # Always add the current line.
969 1220         1443 s/^=/ =/; # so doesn't look like POD
970 1220         2176 push @to_return, "$_\n";
971             # If we need to add a # line directive, do so after incrementing
972 1220         1409 $line++;
973 1220 100 100     3981 if (/%\{/ or /%}/) {
974 4         13 push @to_return, "#line $line \"$filename\"\n";
975             }
976             }
977              
978 75         720 return join('', @to_return);
979             }
980              
981             sub printxsc {
982 1     1 0 3 shift;
983 1         60 $::PDLXSC .= join '',@_;
984             }
985              
986             sub pp_done {
987 1 50   1 1 4 return if $PDL::PP::done; # do only once!
988 1         1 $PDL::PP::done = 1;
989 1 50       4 $::FUNCSPOD = $::DOCUMENTED ? "\n\n=head1 FUNCTIONS\n\n\n\n=cut\n\n\n"
990             : '';
991 1 50       4 print "DONE!\n" if $::PP_VERBOSE;
992 1 50       2 print "Inline running PDL::PP version $PDL::PP::VERSION...\n" if nopm();
993 1 50       9 (my $fh = FileHandle->new(">$::PDLPREF.xs")) or die "Couldn't open xs file\n";
994 1         193 my $pdl_boot = PDL::Core::Dev::PDL_BOOT('PDL', $::PDLMOD); # don't hardcode in more than one place
995              
996 1         78 $fh->print(pp_line_numbers(__LINE__, qq%
997             /*
998             * THIS FILE WAS GENERATED BY PDL::PP! Do not modify!
999             */
1000              
1001             #define PDL_COMMENT(comment)
1002             PDL_COMMENT("This preprocessor symbol is used to add commentary in the PDL ")
1003             PDL_COMMENT("autogenerated code. Normally, one would use typical C-style ")
1004             PDL_COMMENT("multiline comments (i.e. /* comment */). However, because such ")
1005             PDL_COMMENT("comments do not nest, it's not possible for PDL::PP users to ")
1006             PDL_COMMENT("comment-out sections of code using multiline comments, as is ")
1007             PDL_COMMENT("often the practice when debugging, for example. So, when you ")
1008             PDL_COMMENT("see something like this: ")
1009             PDL_COMMENT(" ")
1010             PDL_COMMENT("Memory access")
1011             PDL_COMMENT(" ")
1012             PDL_COMMENT("just think of it as a C multiline comment like: ")
1013             PDL_COMMENT(" ")
1014             PDL_COMMENT(" /* Memory access */ ")
1015              
1016             #include "EXTERN.h"
1017             #include "perl.h"
1018             #include "XSUB.h"
1019             #include "pdl.h"
1020             #include "pdlcore.h"
1021             static Core* PDL; PDL_COMMENT("Structure hold core C functions")
1022             static int __pdl_debugging = 0;
1023             static int __pdl_boundscheck = 0;
1024             static SV* CoreSV; PDL_COMMENT("Gets pointer to perl var holding core structure")
1025              
1026             #if ! $PP::boundscheck
1027             # define PP_INDTERM(max, at) at
1028             #else
1029             # define PP_INDTERM(max, at) (__pdl_boundscheck? PDL->safe_indterm(max,at, __FILE__, __LINE__) : at)
1030             #endif
1031              
1032             $::PDLXSC
1033              
1034             MODULE = $::PDLMOD PACKAGE = $::PDLMOD
1035              
1036             PROTOTYPES: ENABLE
1037              
1038             int
1039             set_debugging(i)
1040             int i;
1041             CODE:
1042             RETVAL = __pdl_debugging;
1043             __pdl_debugging = i;
1044             OUTPUT:
1045             RETVAL
1046              
1047             int
1048             set_boundscheck(i)
1049             int i;
1050             CODE:
1051             if (! $PP::boundscheck)
1052             warn("Bounds checking is disabled for $::PDLMOD");
1053             RETVAL = __pdl_boundscheck;
1054             __pdl_boundscheck = i;
1055             OUTPUT:
1056             RETVAL
1057              
1058              
1059             MODULE = $::PDLMOD PACKAGE = $::PDLOBJ
1060              
1061             $::PDLXS
1062              
1063             BOOT:
1064              
1065             PDL_COMMENT("Get pointer to structure of core shared C routines")
1066             PDL_COMMENT("make sure PDL::Core is loaded")
1067             $pdl_boot
1068             $::PDLXSBOOT
1069             %));
1070              
1071 1 50       103 unless (nopm) {
1072 1         7 $::PDLPMISA = "'".join("','",@::PDLPMISA)."'";
1073 1 50       7 $::PDLBEGIN = "BEGIN {\n$::PDLBEGIN\n}"
1074             unless $::PDLBEGIN =~ /^\s*$/;
1075 1 50       8 ($fh = FileHandle->new(">$::PDLPREF.pm")) or die "Couldn't open pm file\n";
1076              
1077 1         153 $fh->print(qq%
1078             #
1079             # GENERATED WITH PDL::PP! Don't modify!
1080             #
1081             package $::PDLPACK;
1082              
1083             \@EXPORT_OK = qw( $::PDLPMROUT);
1084             \%EXPORT_TAGS = (Func=>[\@EXPORT_OK]);
1085              
1086             use PDL::Core$::PDLCOREIMPORT;
1087             use PDL::Exporter;
1088             use DynaLoader;
1089              
1090              
1091             $::PDL_IFBEGINWRAP[0]
1092             $::PDLVERSIONSET
1093             \@ISA = ( $::PDLPMISA );
1094             push \@PDL::Core::PP, __PACKAGE__;
1095             bootstrap $::PDLMOD $::PDLMODVERSION;
1096             $::PDL_IFBEGINWRAP[-1]
1097              
1098             $::PDLBEGIN
1099              
1100             $::PDLPM{Top}
1101              
1102             $::FUNCSPOD
1103              
1104             $::PDLPM{Middle};
1105              
1106             $::PDLPM{Bot}
1107              
1108             # Exit with OK status
1109              
1110             1;
1111              
1112             %); # end of print
1113             } # unless (nopm) {...
1114             } # end pp_done
1115              
1116             sub pp_def {
1117 2     2 1 112 my($name,%obj) = @_;
1118              
1119 2 50       10 print "*** Entering pp_def for $name\n" if $::PP_VERBOSE;
1120            
1121             # See if the 'name' is multiline, in which case we extract the
1122             # name and add the FullDoc field
1123 2 50       9 if ($name =~ /\n/) {
1124 0         0 my $fulldoc = $name;
1125             # See if the very first thing is a word. That is going to be the
1126             # name of the function under consideration
1127 0 0       0 if ($fulldoc =~ s/^(\w+)//) {
    0          
1128 0         0 $name = $1;
1129             }
1130             elsif ($fulldoc =~ /=head2 (\w+)/) {
1131 0         0 $name = $1;
1132             }
1133             else {
1134 0         0 croak('Unable to extract name');
1135             }
1136 0         0 $obj{FullDoc} = $fulldoc;
1137             }
1138            
1139 2         9 $obj{Name} = $name;
1140 2         11 translate(\%obj,$PDL::PP::deftbl);
1141              
1142             print "Output of translate for $name:\n" . Dumper(\%obj) . "\n"
1143 1 0 33     3 if exists $obj{Dump} and $obj{Dump} and $::PP_VERBOSE;
      0        
1144              
1145             croak("ERROR: No FreeFunc for pp_def=$name!\n")
1146 1 50       5 unless exists $obj{FreeFunc}; # and $obj{FreeFunc};
1147              
1148 1         59 PDL::PP->printxsc(join "\n\n",@obj{'StructDecl','RedoDimsFunc',
1149             'CopyFunc',
1150             'ReadDataFunc','WriteBackDataFunc',
1151             'FreeFunc',
1152             'FooFunc',
1153             'VTableDef','NewXSInPrelude',
1154             }
1155             );
1156 1         7 PDL::PP->printxs($obj{NewXSCode});
1157 1         6 pp_add_boot($obj{XSBootCode} . $obj{BootSetNewXS});
1158 1         5 PDL::PP->pp_add_exported($name);
1159 1 50       6 PDL::PP::pp_addpm("\n".$obj{PdlDoc}."\n") if $obj{PdlDoc};
1160 1         4 PDL::PP::pp_addpm($obj{PMCode});
1161 1         3 PDL::PP::pp_addpm($obj{PMFunc}."\n");
1162              
1163 1 50       62 print "*** Leaving pp_def for $name\n" if $::PP_VERBOSE;
1164             }
1165              
1166             # marks this module as deprecated. This handles the user warnings, and adds a
1167             # notice into the documentation. Can take a {infavor => "newmodule"} option
1168             sub pp_deprecate_module
1169             {
1170 0     0 1 0 my $options;
1171 0 0       0 if( ref $_[0] eq 'HASH' ) { $options = shift; }
  0         0  
1172 0         0 else { $options = { @_ }; }
1173              
1174 0         0 my $infavor;
1175              
1176 0 0 0     0 if( $options && ref $options eq 'HASH' && $options->{infavor} )
      0        
1177             {
1178 0         0 $infavor = $options->{infavor};
1179             }
1180              
1181 0         0 my $mod = $::PDLMOD;
1182 0         0 my $envvar = 'PDL_SUPPRESS_DEPRECATION_WARNING__' . uc $mod;
1183 0         0 $envvar =~ s/::/_/g;
1184              
1185 0         0 my $warning_main =
1186             "$mod is deprecated.";
1187 0 0       0 $warning_main .=
1188             " Please use $infavor instead." if $infavor;
1189              
1190 0         0 my $warning_suppression_runtime =
1191             "This module will be removed in the future; please update your code.\n" .
1192             "Set the environment variable $envvar\n" .
1193             "to suppress this warning\n";
1194              
1195 0         0 my $warning_suppression_pod =
1196             "A warning will be generated at runtime upon a C of this module\n" .
1197             "This warning can be suppressed by setting the $envvar\n" .
1198             "environment variable\n";
1199              
1200 0         0 my $deprecation_notice = <
1201             XXX=head1 DEPRECATION NOTICE
1202              
1203             $warning_main
1204             $warning_suppression_pod
1205              
1206             XXX=cut
1207              
1208             EOF
1209 0         0 $deprecation_notice =~ s/^XXX=/=/gms;
1210 0         0 pp_addpm( {At => 'Top'}, $deprecation_notice );
1211              
1212 0         0 pp_addpm {At => 'Top'}, <
1213             warn \"$warning_main\n$warning_suppression_runtime\" unless \$ENV{$envvar};
1214             EOF
1215              
1216              
1217             }
1218              
1219             # Worst memleaks: not freeing things at redodims or
1220             # final free time (thread, dimmed things).
1221              
1222 2     2   24 use Carp;
  2         5  
  2         290  
1223             $SIG{__DIE__} = sub {print Carp::longmess(@_); die;}
1224             if $::PP_VERBOSE; # seems to give us trouble with 5.6.1
1225              
1226 2     2   1062 use PDL::PP::Signature;
  2         6  
  2         109  
1227 2     2   23 use PDL::PP::Dims;
  2         5  
  2         63  
1228 2     2   986 use PDL::PP::CType;
  2         6  
  2         71  
1229 2     2   1088 use PDL::PP::XS;
  2         7  
  2         68  
1230 2     2   751 use PDL::PP::SymTab;
  2         4  
  2         63  
1231 2     2   1193 use PDL::PP::PDLCode;
  2         5  
  2         20046  
1232              
1233             $|=1;
1234              
1235             #
1236             # This is ripped from xsubpp to ease the parsing of the typemap.
1237             #
1238             our $proto_re = "[" . quotemeta('\$%&*@;[]') . "]" ;
1239              
1240             sub ValidProtoString ($)
1241             {
1242 0     0 0 0 my($string) = @_ ;
1243              
1244 0 0       0 if ( $string =~ /^$proto_re+$/ ) {
1245 0         0 return $string ;
1246             }
1247              
1248 0         0 return 0 ;
1249             }
1250              
1251             sub C_string ($)
1252             {
1253 0     0 0 0 my($string) = @_ ;
1254              
1255 0         0 $string =~ s[\\][\\\\]g ;
1256 0         0 $string ;
1257             }
1258              
1259             sub TrimWhitespace
1260             {
1261 0     0 0 0 $_[0] =~ s/^\s+|\s+$//go ;
1262             }
1263             sub TidyType
1264             {
1265 0     0 0 0 local ($_) = @_ ;
1266              
1267             # rationalise any '*' by joining them into bunches and removing whitespace
1268 0         0 s#\s*(\*+)\s*#$1#g;
1269 0         0 s#(\*+)# $1 #g ;
1270              
1271             # change multiple whitespace into a single space
1272 0         0 s/\s+/ /g ;
1273              
1274             # trim leading & trailing whitespace
1275 0         0 TrimWhitespace($_) ;
1276              
1277 0         0 $_ ;
1278             }
1279              
1280              
1281              
1282             #------------------------------------------------------------------------------
1283             # Typemap handling in PP.
1284             #
1285             # This subroutine does limited input typemap conversion.
1286             # Given a variable name (to set), its type, and the source
1287             # for the variable, returns the correct input typemap entry.
1288             # Original version: D. Hunt 4/13/00 - Current version J. Brinchmann (06/05/05)
1289             #
1290             # This is an extended typemap handler from the one earlier written by
1291             # Doug Hunt. It should work exactly as the older version, but with extensions.
1292             # Instead of handling a few special cases explicitly we now use Perl's
1293             # built-in typemap handling using code taken straight from xsubpp.
1294             #
1295             # I have infact kept the old part of the code here because I belive any
1296             # subsequent hackers might find it very helpful to refer to this code to
1297             # understand what the following does. So here goes:
1298             #
1299             # ------------ OLD TYPEMAP PARSING: ------------------------
1300             #
1301             # # Note that I now just look at the basetype. I don't
1302             # # test whether it is a pointer to the base type or not.
1303             # # This is done because it is simpler and I know that the otherpars
1304             # # belong to a restricted set of types. I know a char will really
1305             # # be a char *, for example. I also know that an SV will be an SV *.
1306             # # yes, but how about catching syntax errors in OtherPars (CS)?
1307             # # shouldn't we really parse the perl typemap (we can steal the code
1308             # # from xsubpp)?
1309             #
1310             # my $OLD_PARSING=0;
1311             # if ($OLD_PARSING) {
1312             # my %typemap = (char => "(char *)SvPV($arg,PL_na)",
1313             # short => "(short)SvIV($arg)",
1314             # int => "(int)SvIV($arg)",
1315             # long => "(long)SvIV($arg)",
1316             # double => "(double)SvNV($arg)",
1317             # float => "(float)SvNV($arg)",
1318             # SV => "$arg",
1319             # );
1320             # my $basetype = $type->{Base};
1321             # $basetype =~ s/\s+//g; # get rid of whitespace
1322             #
1323             # die "Cannot find $basetype in my (small) typemap" unless exists($typemap{$basetype});
1324             # return ($typemap{$basetype});
1325             # }
1326             #
1327             #--------- END OF THE OLD CODE ---------------
1328             #
1329             # The code loads the typemap from the Perl typemap using the loading logic of
1330             # xsubpp. Do note that I made the assumption that
1331             # $Config{}installprivlib}/ExtUtils was the right root directory for the search.
1332             # This could break on some systems?
1333             #
1334             # Also I do _not_ parse the Typemap argument from ExtUtils::MakeMaker because I don't
1335             # know how to catch it here! This would be good to fix! It does look for a file
1336             # called typemap in the current directory however.
1337             #
1338             # The parsing of the typemap is mechanical and taken straight from xsubpp and
1339             # the resulting hash lookup is then used to convert the input type to the
1340             # necessary outputs (as seen in the old code above)
1341             #
1342             # JB 06/05/05
1343             #
1344             sub typemap {
1345 0     0 0 0 my $oname = shift;
1346 0         0 my $type = shift;
1347 0         0 my $arg = shift;
1348              
1349             #
1350             # Modification to parse Perl's typemap here.
1351             #
1352             # The default search path for the typemap taken from xsubpp. It seems it is
1353             # necessary to prepend the installprivlib/ExtUtils directory to find the typemap.
1354             # It is not clear to me how this is to be done.
1355             #
1356 0         0 my ($typemap, $mode, $junk, $current, %input_expr,
1357             %proto_letter, %output_expr, %type_kind);
1358              
1359             # according to MM_Unix 'privlibexp' is the right directory
1360             # seems to work even on OS X (where installprivlib breaks things)
1361             # if this does not work portably we should split out the typemap finding code
1362             # and make it as complex as necessary + save the typemap location
1363             # in the PDL::Config hash
1364 0         0 my $_rootdir = $Config{privlibexp}.'/ExtUtils/';
1365             # print "_rootdir set to '$_rootdir'\n";
1366              
1367             # First the system typemaps..
1368 0         0 my @tm = ($_rootdir.'../../../../lib/ExtUtils/typemap',
1369             $_rootdir.'../../../lib/ExtUtils/typemap',
1370             $_rootdir.'../../lib/ExtUtils/typemap',
1371             $_rootdir.'../../../typemap',
1372             $_rootdir.'../../typemap', $_rootdir.'../typemap',
1373             $_rootdir.'typemap');
1374             # Finally tag onto the end, the current directory typemap. Ideally we should here pick
1375             # up the TYPEMAPS flag from ExtUtils::MakeMaker, but a) I don't know how and b)
1376             # it is only a slight inconvenience hopefully!
1377             #
1378             # Note that the OUTPUT typemap is unlikely to be of use here, but I have kept
1379             # the source code from xsubpp for tidiness.
1380 0         0 push @tm, 'typemap';
1381 0         0 my $foundtm = 0;
1382 0         0 foreach $typemap (@tm) {
1383 0 0       0 next unless -f $typemap ;
1384             # skip directories, binary files etc.
1385 0 0       0 warn("Warning: ignoring non-text typemap file '$typemap'\n"), next
1386             unless -T $typemap ;
1387 0         0 $foundtm = 1;
1388 0 0       0 open(TYPEMAP, $typemap)
1389             or warn ("Warning: could not open typemap file '$typemap': $!\n"), next;
1390 0         0 $mode = 'Typemap';
1391 0         0 $junk = "" ;
1392 0         0 $current = \$junk;
1393 0         0 while () {
1394 0 0       0 next if /^\s*#/;
1395 0         0 my $line_no = $. + 1;
1396 0 0       0 if (/^INPUT\s*$/) { $mode = 'Input'; $current = \$junk; next; }
  0         0  
  0         0  
  0         0  
1397 0 0       0 if (/^OUTPUT\s*$/) { $mode = 'Output'; $current = \$junk; next; }
  0         0  
  0         0  
  0         0  
1398 0 0       0 if (/^TYPEMAP\s*$/) { $mode = 'Typemap'; $current = \$junk; next; }
  0         0  
  0         0  
  0         0  
1399 0 0       0 if ($mode eq 'Typemap') {
    0          
    0          
1400 0         0 chomp;
1401 0         0 my $line = $_ ;
1402 0         0 TrimWhitespace($_) ;
1403             # skip blank lines and comment lines
1404 0 0 0     0 next if /^$/ or /^#/ ;
1405 0 0       0 my($t_type,$kind, $proto) = /^\s*(.*?\S)\s+(\S+)\s*($proto_re*)\s*$/ or
1406             warn("Warning: File '$typemap' Line $. '$line' TYPEMAP entry needs 2 or 3 columns\n"), next;
1407 0         0 $t_type = TidyType($t_type) ;
1408 0         0 $type_kind{$t_type} = $kind ;
1409             # prototype defaults to '$'
1410 0 0       0 $proto = "\$" unless $proto ;
1411 0 0       0 warn("Warning: File '$typemap' Line $. '$line' Invalid prototype '$proto'\n")
1412             unless ValidProtoString($proto) ;
1413 0         0 $proto_letter{$t_type} = C_string($proto) ;
1414             }
1415             elsif (/^\s/) {
1416 0         0 $$current .= $_;
1417             }
1418             elsif ($mode eq 'Input') {
1419 0         0 s/\s+$//;
1420 0         0 $input_expr{$_} = '';
1421 0         0 $current = \$input_expr{$_};
1422             }
1423             else {
1424 0         0 s/\s+$//;
1425 0         0 $output_expr{$_} = '';
1426 0         0 $current = \$output_expr{$_};
1427             }
1428             }
1429 0         0 close(TYPEMAP);
1430             }
1431 0 0       0 carp "**CRITICAL** PP found no typemap in $_rootdir/typemap; this will cause problems..."
1432             unless $foundtm;
1433              
1434             #
1435             # Do checks...
1436             #
1437             # First reconstruct the type declaration to look up in type_kind
1438 0         0 my $full_type=TidyType($type->get_decl('')); # Skip the variable name
1439 0 0       0 die "The type =$full_type= does not have a typemap entry!\n" unless exists($type_kind{$full_type});
1440 0         0 my $typemap_kind = $type_kind{$full_type};
1441             # Look up the conversion from the INPUT typemap. Note that we need to do some
1442             # massaging of this.
1443 0         0 my $input = $input_expr{$typemap_kind};
1444             # Remove all before =:
1445 0         0 $input =~ s/^(.*?)=\s*//; # This should not be very expensive
1446             # Replace $arg with $arg
1447 0         0 $input =~ s/\$arg/$arg/;
1448             # And type with $full_type
1449 0         0 $input =~ s/\$type/$full_type/;
1450              
1451 0         0 return ($input);
1452             }
1453              
1454              
1455             sub identity2priv {
1456 0     0 0 0 PDL::PP::pp_line_numbers(__LINE__, '
1457             int i;
1458             $SETNDIMS($PARENT(ndims));
1459             for(i=0; i<$CHILD(ndims); i++) {
1460             $CHILD(dims[i]) = $PARENT(dims[i]);
1461             }
1462             $SETDIMS();
1463             $SETDELTATHREADIDS(0);
1464             ');
1465             }
1466              
1467             sub pdimexpr2priv {
1468 0     0 0 0 my($pdimexpr,$hdr,$dimcheck) = @_;
1469 0         0 $pdimexpr =~ s/\$CDIM\b/i/g;
1470 0         0 PDL::PP::pp_line_numbers(__LINE__, '
1471             int i,cor;
1472             '.$dimcheck.'
1473             $SETNDIMS($PARENT(ndims));
1474             $DOPRIVDIMS();
1475             $PRIV(offs) = 0;
1476             for(i=0; i<$CHILD(ndims); i++) {
1477             cor = '.$pdimexpr.';
1478             $CHILD(dims[i]) = $PARENT(dims[cor]);
1479             $PRIV(incs[i]) = $PARENT(dimincs[cor]);
1480              
1481             }
1482             $SETDIMS();
1483             $SETDELTATHREADIDS(0);
1484             ');
1485             }
1486              
1487             # something to do with copying values between parent and children
1488             #
1489             # we can NOT assume that PARENT and CHILD have the same type,
1490             # hence the version for bad code
1491             #
1492             # NOTE: we use the same code for 'good' and 'bad' cases - it's
1493             # just that when we use it for 'bad' data, we have to change the
1494             # definition of the EQUIVCPOFFS macro - see the Code rule
1495             #
1496             sub equivcpoffscode {
1497 0     0 0 0 PDL::PP::pp_line_numbers(__LINE__,
1498             'PDL_Indx i;
1499             for(i=0; i<$CHILD_P(nvals); i++) {
1500             $EQUIVCPOFFS(i,i);
1501             }');
1502              
1503             } # sub: equivcpoffscode()
1504              
1505             # Pars -> ParNames, Parobjs
1506             #
1507             # XXX
1508             # - the need for BadFlag is due to hacked get_xsdatapdecl()
1509             # in PP/PdlParObj and because the PdlParObjs are created by
1510             # PDL::PP::Signature (Doug Burke 07/08/00)
1511             sub Pars_nft {
1512 2     2 0 5 my($str,$badflag) = @_;
1513 2         20 my $sig = PDL::PP::Signature->new($str,$badflag);
1514 2         18 return ($sig->names,$sig->objs,1);
1515             }
1516              
1517             # ParNames,Parobjs -> DimObjs
1518             sub ParObjs_DimObjs {
1519 2     2 0 6 my($pnames,$pobjs) = @_;
1520 2         19 my ($dimobjs) = PDL::PP::PdlDimsObj->new();
1521 2         7 for(@$pnames) {
1522 2         11 $pobjs->{$_}->add_inds($dimobjs);
1523             }
1524 2         6 return ($dimobjs);
1525             }
1526              
1527             # Eliminate whitespace entries
1528 3 100   3 0 54 sub nospacesplit {map {/^\s*$/?():$_} split $_[0],$_[1]}
  4         17  
1529              
1530             sub OtherPars_nft {
1531 3     3 0 8 my($otherpars,$dimobjs) = @_;
1532 3         6 my(@names,%types,$type);
1533             # support 'int ndim => n;' syntax
1534 3         10 for (nospacesplit ';',$otherpars) {
1535 3 50       16 if (/^\s*([^=]+)\s*=>\s*(\S+)\s*$/) {
    50          
1536 0         0 my ($ctype,$dim) = ($1,$2);
1537 0         0 $ctype =~ s/(\S+)\s+$/$1/; # get rid of trailing ws
1538 0 0       0 print "OtherPars: setting dim '$dim' from '$ctype'\n" if $::PP_VERBOSE;
1539 0         0 $type = C::Type->new(undef,$ctype);
1540             croak "can't set unknown dimension"
1541 0 0       0 unless defined($dimobjs->{$dim});
1542 0         0 $dimobjs->{$dim}->set_from($type);
1543             } elsif(/^\s*pdl\s+\*\s*(\w+)$/) {
1544             # It is a piddle -> make it a controlling one.
1545 0         0 die("Not supported yet");
1546             } else {
1547 3         9 $type = C::Type->new(undef,$_);
1548             }
1549 3         8 my $name = $type->protoname;
1550 3 50       16 if ($name =~ /$INVALID_OTHERPARS_RE/) {
1551 0         0 croak "Invalid OtherPars name: $name";
1552             }
1553 3         7 push @names,$name;
1554 3         7 $types{$name} = $type;
1555             }
1556 3         11 return (\@names,\%types);
1557             }
1558              
1559             sub NXArgs {
1560 2     2 0 7 my($parnames,$parobjs,$onames,$oobjs) = @_;
1561 2         10 my $pdltype = C::Type->new(undef,"pdl *__foo__");
1562             my $nxargs = [
1563 2         10 ( map {[$_,$pdltype]} @$parnames ),
1564 2         6 ( map {[$_,$oobjs->{$_}]} @$onames )
  0         0  
1565             ];
1566 2         5 return $nxargs;
1567             }
1568              
1569             # XXX
1570             # - the need for BadFlag is due to hacked get_xsdatapdecl()
1571             # in PP/PdlParObj and because the PdlParObjs are created by
1572             # PDL::PP::Signature (Doug Burke 07/08/00)
1573             sub NewParentChildPars {
1574 0     0 0 0 my($p2child,$name,$badflag) = @_;
1575 0         0 return (Pars_nft("PARENT(); [oca]CHILD();",$badflag),0,"${name}_NN");
1576             }
1577              
1578             # XXX
1579             # - the need for BadFlag is due to hacked get_xsdatapdecl()
1580             # in PP/PdlParObj and because the PdlParObjs are created by
1581             # PDL::PP::Signature (Doug Burke 07/08/00)
1582             #
1583             # however, it looks like this isn't being used anymore,
1584             # so commenting out.
1585             #
1586             #sub ParentChildPars {
1587             # my($p2child,$name,$badflag) = @_;
1588             # return (Pars_nft("PARENT(); [oca]CHILD();",$badflag),0,"${name}_XX",
1589             # "
1590             # *$name = \\&PDL::$name;
1591             # sub PDL::$name {
1592             # my \$this = shift;
1593             # my \$foo=\$this->null;
1594             # PDL::${name}_XX(\$this,\$foo,\@_);
1595             # \$foo
1596             # }
1597             # ");
1598             #}
1599              
1600             sub mkstruct {
1601 1     1 0 4 my($pnames,$pobjs,$comp,$priv,$name) = @_;
1602 1         2 my $npdls = $#$pnames+1;
1603 1         8 PDL::PP::pp_line_numbers(__LINE__, qq{typedef struct $name {
1604             PDL_TRANS_START($npdls);
1605             $priv
1606             $comp
1607             char __ddone; PDL_COMMENT("Dims done")
1608             } $name;});
1609             }
1610              
1611             sub def_vtable {
1612 1     1 0 5 my($vname,$sname,$rdname,$rfname,$wfname,$cpfname,$ffname,
1613             $pnames,$pobjs,$affine_ok,$foofname) = @_;
1614 1         2 my $nparents = 0 + grep {! $pobjs->{$_}->{FlagW}} @$pnames;
  1         5  
1615 1 50       3 my $aff = ($affine_ok ? "PDL_TPDL_VAFFINE_OK" : 0);
1616 1         2 my $npdls = scalar @$pnames;
1617 1         3 my $join_flags = join",",map {$pobjs->{$pnames->[$_]}->{FlagPhys} ?
1618 1 50       5 0 : $aff} 0..$npdls-1;
1619 1 50       10 if($Config{cc} eq 'cl') {
1620 0 0       0 $join_flags = '""' if $join_flags eq '';
1621             }
1622 1         13 PDL::PP::pp_line_numbers(__LINE__, "static char ${vname}_flags[] =
1623             { ". $join_flags . "};
1624             pdl_transvtable $vname = {
1625             0,0, $nparents, $npdls, ${vname}_flags,
1626             $rdname, $rfname, $wfname,
1627             $ffname,NULL,NULL,$cpfname,
1628             sizeof($sname),\"$vname\"
1629             };");
1630             }
1631              
1632             sub sort_pnobjs {
1633 2     2 0 6 my($pnames,$pobjs) = @_;
1634 2         4 my (@nn);
1635 2 50       5 for(@$pnames) { push ( @nn, $_ ) unless $pobjs->{$_}{FlagW}; }
  2         22  
1636 2 50       7 for(@$pnames) { push ( @nn, $_ ) if $pobjs->{$_}{FlagW}; }
  2         21  
1637 2         5 my $no = 0;
1638 2         5 for(@nn) { $pobjs->{$_}{Number} = $no++; }
  2         6  
1639 2         7 return (\@nn,$pobjs);
1640             }
1641              
1642             # XXX __privtrans explicit :(
1643             sub wrap_vfn {
1644 4     4 0 10 my($code,$hdrinfo,$rout,$p2child,$name) = @_;
1645 4 100       10 my $type = ($name eq "copy" ? "pdl_trans *" : "void");
1646 4         6 my $sname = $hdrinfo->{StructName};
1647 4 50       8 my $oargs = ($name eq "foo" ? ",int i1,int i2,int i3" : "");
1648              
1649             # print "$rout\_$name: $p2child\n";
1650 4         6 my $p2decl = '';
1651             # Put p2child in simple boolean context rather than strict numerical equality
1652 4 50       8 if ( $p2child ) {
1653 0         0 $p2decl =
1654             PDL::PP::pp_line_numbers(__LINE__, "pdl *__it = ((pdl_trans_affine *)(__tr))->pdls[1]; pdl *__parent = __tr->pdls[0];");
1655 0 0       0 if ( $name eq "redodims" ) {
1656 0         0 $p2decl .= '
1657             if (__parent->hdrsv && (__parent->state & PDL_HDRCPY)) {
1658             PDL_COMMENT("call the perl routine _hdr_copy.")
1659             int count;
1660              
1661             dSP;
1662             ENTER ;
1663             SAVETMPS ;
1664             PUSHMARK(SP) ;
1665             XPUSHs( sv_mortalcopy((SV*)__parent->hdrsv) );
1666             PUTBACK ;
1667             count = call_pv("PDL::_hdr_copy",G_SCALAR);
1668             SPAGAIN ;
1669             if(count != 1)
1670             croak("PDL::_hdr_copy didn\'t return a single value - please report this bug (B).");
1671              
1672             { PDL_COMMENT("convenience block for tmp var")
1673             SV *tmp = (SV *) POPs ;
1674             __it->hdrsv = (void*) tmp;
1675             if(tmp != &PL_sv_undef )
1676             (void)SvREFCNT_inc(tmp);
1677             }
1678              
1679             __it->state |= PDL_HDRCPY;
1680              
1681             FREETMPS ;
1682             LEAVE ;
1683             }
1684             ';
1685             }
1686             } # if: $p2child == 1
1687              
1688             qq|$type $rout(pdl_trans *__tr $oargs) {
1689             int __dim;
1690             $sname *__privtrans = ($sname *) __tr;
1691             $p2decl
1692             {
1693             $code
1694             }
1695             }
1696 4         58 |;
1697              
1698             } # sub: wrap_vfn()
1699              
1700             sub makesettrans {
1701 2     2 0 6 my($pnames,$pobjs,$symtab) = @_;
1702 2         8 my $trans = $symtab->get_symname('_PDL_ThisTrans');
1703 2         4 my $no=0;
1704             PDL::PP::pp_line_numbers(__LINE__, (join '',map {
1705 2         6 "$trans->pdls[".($no++)."] = $_;\n"
  2         18  
1706             } @$pnames).
1707             "PDL->make_trans_mutual((pdl_trans *)$trans);\n");
1708             }
1709              
1710             sub CopyOtherPars {
1711 1     1 0 2 my($onames,$otypes,$symtab) = @_; my $repr;
  1         2  
1712 1         4 my $sname = $symtab->get_symname('_PDL_ThisTrans');
1713 1         3 for(@$onames) {
1714 0         0 $repr .= $otypes->{$_}->get_copy("$_","$sname->$_");
1715             }
1716 1         3 PDL::PP::pp_line_numbers(__LINE__, $repr);
1717             }
1718              
1719             sub mkxscat {
1720 0     0 0 0 my($glb,$xs_c_headers,$hdr,@bits) = @_;
1721 0         0 my($boot,$prelude,$str);
1722 0 0       0 if($glb) {
1723 0         0 $prelude = join '' => ($xs_c_headers->[0], @bits, $xs_c_headers->[1]);
1724 0         0 $boot = $xs_c_headers->[3];
1725 0         0 $str = "$hdr\n";
1726             } else {
1727 0         0 my $xscode = join '' => @bits;
1728 0         0 $str = "$hdr CODE:\n { $xscode XSRETURN(0);\n}\n\n";
1729             }
1730 0         0 $str =~ s/(\s*\n)+/\n/g;
1731 0         0 (PDL::PP::pp_line_numbers(__LINE__, $str),$boot,$prelude)
1732             }
1733              
1734             sub mkVarArgsxscat {
1735 1     1 0 5 my($glb,$xs_c_headers,$hdr,@bits) = @_;
1736 1         3 my($boot,$prelude,$str);
1737 1 50       3 if($glb) {
1738 0         0 $prelude = join '' => ($xs_c_headers->[0], @bits, $xs_c_headers->[1]);
1739 0         0 $boot = $xs_c_headers->[3];
1740 0         0 $str = "$hdr\n";
1741             } else {
1742 1         24 my $xscode = join '' => @bits;
1743 1         18 $str = "$hdr \n { $xscode \n}\n\n";
1744             }
1745 1         210 $str =~ s/(\s*\n)+/\n/g;
1746 1         6 (PDL::PP::pp_line_numbers(__LINE__, $str),$boot,$prelude)
1747             }
1748              
1749              
1750             sub MakeNows {
1751 2     2 0 6 my($pnames, $symtab) = @_;
1752 2         6 my $str = "\n";
1753 2         4 for(@$pnames) { $str .= "$_ = PDL->make_now($_);\n"; }
  2         9  
1754 2         6 PDL::PP::pp_line_numbers(__LINE__, $str);
1755             }
1756              
1757 2     2 0 8 sub Sym2Loc { PDL::PP::pp_line_numbers(__LINE__, $_[0]->decl_locals()) }
1758              
1759             sub MkPrivStructInit {
1760 2     2 0 8 my( $symtab, $vtable, $affflag, $nopdlthread ) = @_;
1761 2         10 my $sname = $symtab->get_symname('_PDL_ThisTrans');
1762              
1763 2         5 my $ci = ' ';
1764 2 50       34 PDL::PP::pp_line_numbers(__LINE__,
1765             "\n${ci}$sname = malloc(sizeof(*$sname)); memset($sname, 0, sizeof(*$sname));\n" .
1766             ($nopdlthread ? "" : "${ci}PDL_THR_CLRMAGIC(&$sname->__pdlthread);\n") .
1767             "${ci}PDL_TR_SETMAGIC($sname);\n" .
1768             "${ci}$sname->flags = $affflag;\n" .
1769             "${ci}$sname->__ddone = 0;\n" .
1770             "${ci}$sname->vtable = &$vtable;\n" .
1771             "${ci}$sname->freeproc = PDL->trans_mallocfreeproc;\n")
1772              
1773             } # sub: MkPrivStructInit()
1774              
1775             sub MkDefSyms {
1776 2     2 0 20 return SymTab->new(
1777             _PDL_ThisTrans => ["__privtrans",C::Type->new(undef,"$_[0] *foo")],
1778             );
1779             }
1780              
1781             sub AddArgsyms {
1782 2     2 0 8 my($symtab,$args) = @_;
1783             $symtab->add_params(
1784 2         4 map {($_->[0],$_->[0])} @$args
  2         13  
1785             );
1786 2         19 return $symtab;
1787             }
1788              
1789             sub indent($$) {
1790 4     4 0 9 my ($text,$ind) = @_;
1791 4         47 $text =~ s/^(.*)$/$ind$1/mg;
1792 4         16 return $text;
1793             }
1794              
1795             # This subroutine generates the XS code needed to call the perl 'initialize'
1796             # routine in order to create new output PDLs
1797             sub callPerlInit {
1798 4     4 0 8 my $names = shift; # names of variables to initialize
1799 4         7 my $ci = shift; # current indenting
1800 4 50       26 my $callcopy = $#_ > -1 ? shift : 0;
1801 4         10 my $ret = '';
1802              
1803 4         9 foreach my $name (@$names) {
1804 0 0       0 unless ($callcopy) { $ret .= << "EOC"}
  0         0  
1805              
1806             if (strcmp(objname,"PDL") == 0) { PDL_COMMENT("shortcut if just PDL")
1807             $name\_SV = sv_newmortal();
1808             $name = PDL->null();
1809             PDL->SetSV_PDL($name\_SV,$name);
1810             if (bless_stash) $name\_SV = sv_bless($name\_SV, bless_stash);
1811             } else {
1812             PUSHMARK(SP);
1813             XPUSHs(sv_2mortal(newSVpv(objname, 0)));
1814             PUTBACK;
1815             perl_call_method(\"initialize\", G_SCALAR);
1816             SPAGAIN;
1817             $name\_SV = POPs;
1818             PUTBACK;
1819             $name = PDL->SvPDLV($name\_SV);
1820             }
1821              
1822             EOC
1823              
1824 0         0 else { $ret .= << "EOD" }
1825              
1826             if (strcmp(objname,"PDL") == 0) { PDL_COMMENT("shortcut if just PDL")
1827             $name\_SV = sv_newmortal();
1828             $name = PDL->null();
1829             PDL->SetSV_PDL($name\_SV,$name);
1830             if (bless_stash) $name\_SV = sv_bless($name\_SV, bless_stash);
1831             } else {
1832             /* XXX should these commented lines be removed? See also a 8 lines down */
1833             /* warn("possibly relying on deprecated automatic copy call in derived class\n")
1834             warn("please modify your initialize method to avoid future problems\n");
1835             */
1836             PUSHMARK(SP);
1837             XPUSHs(parent);
1838             PUTBACK;
1839             perl_call_method(\"copy\", G_SCALAR);
1840             /* perl_call_method(\"initialize\", G_SCALAR); */
1841             SPAGAIN;
1842             $name\_SV = POPs;
1843             PUTBACK;
1844             $name = PDL->SvPDLV($name\_SV);
1845             }
1846             EOD
1847              
1848             } # doreach: $name
1849              
1850 4         11 PDL::PP::pp_line_numbers(__LINE__, indent($ret,$ci));
1851              
1852             } #sub callPerlInit()
1853              
1854             # This subroutine is called when no 'otherpars' exist.
1855             # This writes an XS header which handles variable argument lists,
1856             # thus avoiding the perl layer in calling the routine. D. Hunt 4/11/00
1857             #
1858             # The use of 'DO NOT SET!!' looks ugly.
1859             #
1860             # Removing useless use of hasp2child in this function. DCM Sept 12, 2011
1861             sub VarArgsXSHdr {
1862 2     2 0 10 my($name,$xsargs,$parobjs,$optypes,#$hasp2child,
1863             $pmcode,$hdrcode,$inplacecode,$globalnew,$callcopy,$bitwise) = @_;
1864              
1865             # Don't do var args processing if the user has pre-defined pmcode
1866 2 50       6 return 'DO NOT SET!!' if ($pmcode);
1867              
1868             # don't generate a HDR if globalnew is set
1869             # globalnew implies internal usage, not XS
1870 2 50       7 return undef if $globalnew;
1871              
1872 2         4 my $ci = ' '; # current indenting
1873 2         6 my $pars = join "\n",map {$ci.$_->[1]->get_decl($_->[0]).";"} @$xsargs;
  2         20  
1874              
1875 2         16 my @args = map { $_->[0] } @$xsargs;
  2         10  
1876 2         4 my %out = map { $_ => exists($$parobjs{$_})
1877             && exists($$parobjs{$_}{FlagOut})
1878 2   33     19 && !exists($$parobjs{$_}{FlagCreateAlways})}
1879             @args;
1880 2         7 my %outca = map { $_ => exists($$parobjs{$_})
1881             && exists($$parobjs{$_}{FlagOut})
1882 2   33     25 && exists($$parobjs{$_}{FlagCreateAlways})}
1883             @args;
1884 2   33     6 my %tmp = map { $_ => exists($$parobjs{$_}) && exists($$parobjs{$_}{FlagTemp}) } @args;
  2         30  
1885 2         9 my %other = map { $_ => exists($$optypes{$_}) } @args;
  2         5  
1886              
1887             # remember, othervars *are* input vars
1888 2         7 my $nout = (grep { $_ } values %out);
  2         6  
1889 2         6 my $noutca = (grep { $_ } values %outca);
  2         6  
1890 2         6 my $nother = (grep { $_ } values %other);
  2         4  
1891 2         11 my $ntmp = (grep { $_ } values %tmp);
  2         7  
1892 2         3 my $ntot = @args;
1893 2         6 my $nmaxonstack = $ntot - $noutca;
1894 2         4 my $nin = $ntot - ($nout + $noutca + $ntmp);
1895 2         4 my $ninout = $nin + $nout;
1896 2         4 my $nallout = $nout + $noutca;
1897 2         6 my $usageargs = join (",", @args);
1898              
1899 2         3 $ci = ' '; # Current indenting
1900              
1901             # Generate declarations for SV * variables corresponding to pdl * output variables.
1902             # These are used in creating output and temp variables. One variable (ex: SV * outvar1_SV;)
1903             # is needed for each output and output create always argument
1904 2 50 33     5 my $svdecls = join ("\n", map { "${ci}SV *${_}_SV;" } grep { $out{$_} || $outca{$_} || $tmp{$_} } @args);
  0         0  
  2         19  
1905              
1906 2         6 my @create = (); # The names of variables which need to be created by calling
1907             # the 'initialize' perl routine from the correct package.
1908              
1909 2         4 $ci = ' '; # Current indenting
1910              
1911             # clause for reading in all variables
1912 2         3 my $clause1 = ''; my $cnt = 0;
  2         4  
1913 2         9 foreach my $i ( 0 .. $#args ) {
1914 2         12 my $x = $args[$i];
1915 2 50       10 if ($other{$x}) { # other par
    50          
1916 0         0 $clause1 .= "$ci$x = " . typemap($x, $$optypes{$x}, "ST($cnt)") . ";\n";
1917 0         0 $cnt++;
1918             } elsif ($outca{$x}) {
1919 0         0 push (@create, $x);
1920             } else {
1921 2         9 $clause1 .= "$ci$x = PDL->SvPDLV(ST($cnt));\n";
1922 2         6 $cnt++;
1923             }
1924             }
1925              
1926             # Add code for creating output variables via call to 'initialize' perl routine
1927 2         10 $clause1 .= callPerlInit (\@create, $ci, $callcopy);
1928 2         5 @create = ();
1929              
1930             # clause for reading in input and output vars and creating temps
1931 2         4 my $clause2;
1932             # skip this clause if there are no temps
1933 2 50       7 if ($nmaxonstack == $ninout) {
1934 2         5 $clause2 = '';
1935             } else {
1936 0         0 $clause2 = "\n else if (items == $ninout) { PDL_COMMENT(\"all but temps on stack, read in output, create temps\")" .
1937             " nreturn = $noutca;\n";
1938              
1939 0         0 $cnt = 0;
1940 0         0 foreach my $i ( 0 .. $#args ) {
1941 0         0 my $x = $args[$i];
1942 0 0 0     0 if ($other{$x}) {
    0          
1943 0         0 $clause2 .= "$ci$x = " . typemap($x, $$optypes{$x}, "ST($cnt)") . ";\n";
1944 0         0 $cnt++;
1945             } elsif ($tmp{$x} || $outca{$x}) {
1946             # a temporary or always create variable
1947 0         0 push (@create, $x);
1948             } else { # an input or output variable
1949 0         0 $clause2 .= "$ci$x = PDL->SvPDLV(ST($cnt));\n";
1950 0         0 $cnt++;
1951             }
1952             }
1953              
1954             # Add code for creating output variables via call to 'initialize' perl routine
1955 0         0 $clause2 .= callPerlInit (\@create, $ci, $callcopy);
1956 0         0 $clause2 .= "}\n";
1957 0         0 @create = ();
1958              
1959             }
1960              
1961             # clause for reading in input and creating output and temp vars
1962 2         4 my $clause3 = '';
1963 2         5 $cnt = 0;
1964 2         17 foreach my $i ( 0 .. $#args ) {
1965 2         6 my $x = $args[$i];
1966 2 50 33     23 if ($other{$x}) {
    50 33        
1967 0         0 $clause3 .= "$ci$x = " . typemap($x, $$optypes{$x}, "ST($cnt)") . ";\n";
1968 0         0 $cnt++;
1969             } elsif ($out{$x} || $tmp{$x} || $outca{$x}) {
1970 0         0 push (@create, $x);
1971             } else {
1972 2         9 $clause3 .= "$ci$x = PDL->SvPDLV(ST($cnt));\n";
1973 2         5 $cnt++;
1974             }
1975             }
1976              
1977             # Add code for creating output variables via call to 'initialize' perl routine
1978 2         7 $clause3 .= callPerlInit (\@create, $ci, $callcopy); @create = ();
  2         6  
1979              
1980             # Bitwise ops may get five args
1981 2 50       6 my $bitwise_cond = $bitwise ? " || items == 5" : '';
1982              
1983 2         44 PDL::PP::pp_line_numbers(__LINE__, <
1984              
1985             void
1986             $name(...)
1987             PREINIT:
1988             char *objname = "PDL"; /* XXX maybe that class should actually depend on the value set
1989             by pp_bless ? (CS) */
1990             HV *bless_stash = 0;
1991             SV *parent = 0;
1992             int nreturn;
1993             $svdecls
1994             $pars
1995              
1996             PPCODE:
1997              
1998             {
1999             PDL_COMMENT("Check if you can get a package name for this input value. ")
2000             PDL_COMMENT("It can be either a PDL (SVt_PVMG) or a hash which is a ")
2001             PDL_COMMENT("derived PDL subclass (SVt_PVHV) ")
2002              
2003             if (SvROK(ST(0)) && ((SvTYPE(SvRV(ST(0))) == SVt_PVMG) || (SvTYPE(SvRV(ST(0))) == SVt_PVHV))) {
2004             parent = ST(0);
2005             if (sv_isobject(parent)){
2006             bless_stash = SvSTASH(SvRV(ST(0)));
2007             objname = HvNAME((bless_stash)); PDL_COMMENT("The package to bless output vars into is taken from the first input var")
2008             }
2009             }
2010             if (items == $nmaxonstack) { PDL_COMMENT("all variables on stack, read in output and temp vars")
2011             nreturn = $noutca;
2012             $clause1
2013             }
2014             $clause2
2015             else if (items == $nin$bitwise_cond) { PDL_COMMENT("only input variables on stack, create outputs and temps")
2016             nreturn = $nallout;
2017             $clause3
2018             }
2019              
2020             else {
2021             croak (\"Usage: PDL::$name($usageargs) (you may leave temporaries or output variables out of list)\");
2022             }
2023             }
2024             {
2025             $hdrcode
2026             $inplacecode
2027             }
2028             END
2029              
2030             } # sub: VarArgsXSHdr()
2031              
2032             # This subroutine produces the code which returns output variables
2033             # or leaves them as modified input variables. D. Hunt 4/10/00
2034             sub VarArgsXSReturn {
2035 2     2 0 6 my($xsargs, $parobjs, $globalnew ) = @_;
2036              
2037             # don't generate a HDR if globalnew is set
2038             # globalnew implies internal usage, not XS
2039 2 50       7 return undef if $globalnew;
2040              
2041             # names of output variables (in calling order)
2042 2         5 my @outs;
2043              
2044             # beware of existance tests like this: $$parobjs{$arg->[0]}{FlagOut} !
2045             # this will cause $$parobjs{$arg->[0]} to spring into existance even if $$parobjs{$arg->[0]}{FlagOut}
2046             # does not exist!!
2047 2         5 foreach my $arg (@$xsargs) {
2048 2         6 my $x = $arg->[0];
2049 2 50 33     15 push (@outs, $x) if (exists ($$parobjs{$x}) and exists ($$parobjs{$x}{FlagOut}));
2050             }
2051              
2052 2         44 my $ci = ' '; # Current indenting
2053              
2054 2         18 my $clause1 = '';
2055 2         10 foreach my $i ( 0 .. $#outs ) {
2056 0         0 $clause1 .= ($ci x 2) . "ST($i) = $outs[$i]_SV;\n";
2057             }
2058              
2059 2         16 PDL::PP::pp_line_numbers(__LINE__, <<"END")
2060             ${ci}if (nreturn) {
2061             ${ci} if (nreturn > 0) EXTEND (SP, nreturn );
2062             $clause1
2063             ${ci} XSRETURN(nreturn);
2064             ${ci}} else {
2065             ${ci} XSRETURN(0);
2066             ${ci}}
2067             END
2068              
2069             } # sub: VarArgsXSReturn()
2070              
2071              
2072             sub XSCHdrs {
2073 0     0 0 0 my($name,$pars,$gname) = @_;
2074             # Hmmm, do we need $shortpars at all?
2075             #my $shortpars = join ',',map {$_->[0]} @$pars;
2076 0         0 my $longpars = join ",",map {$_->[1]->get_decl($_->[0])} @$pars;
  0         0  
2077 0         0 return ["void $name($longpars) {","}","",
2078             "PDL->$gname = $name;"];
2079             }
2080              
2081             # abstract the access to the bad value status
2082             # - means we can easily change the representation without too
2083             # many changes
2084             #
2085             # it's also used in one place in PP/PDLCode.pm
2086             # -- there it's hard-coded
2087             #
2088 1     1 0 3 sub set_badflag { PDL::PP::pp_line_numbers(__LINE__, '$PRIV(bvalflag) = 1;' . "\n") }
2089 1     1 0 4 sub clear_badflag { PDL::PP::pp_line_numbers(__LINE__, '$PRIV(bvalflag) = 0;' . "\n") }
2090 1     1 0 2 sub get_badflag { PDL::PP::pp_line_numbers(__LINE__, '$PRIV(bvalflag)') }
2091              
2092 0     0 0 0 sub get_badflag_priv { PDL::PP::pp_line_numbers(__LINE__, '$PRIV(bvalflag)') }
2093              
2094             sub set_badstate {
2095 0     0 0 0 my $pdl = shift;
2096 0         0 PDL::PP::pp_line_numbers(__LINE__, "\$SETPDLSTATEBAD($pdl)")
2097             }
2098              
2099             sub clear_badstate {
2100 0     0 0 0 my $pdl = shift;
2101 0         0 PDL::PP::pp_line_numbers(__LINE__, "\$SETPDLSTATEGOOD($pdl)")
2102             }
2103              
2104             sub get_badstate {
2105 1     1 0 2 my $pdl = shift;
2106 1         11 PDL::PP::pp_line_numbers(__LINE__, "\$ISPDLSTATEBAD($pdl)")
2107             }
2108              
2109             # checks the input piddles to see if the routine
2110             # is being any data containing bad values
2111             #
2112             # if FindBadStatusCode is set, use it,
2113             # otherwise create the code automatically.
2114             #
2115             # - in the automatic code creation,
2116             # if $badflag is 0, rather than being undefined, then
2117             # we issue a warning if any piddles contain bad values
2118             # (and set the bvalflag to 0)
2119             #
2120             # XXX it looks like output piddles are included in the
2121             # check. I *think* this is just wasted code, but I'm
2122             # not sure.
2123             #
2124             sub findbadstatus {
2125 1     1 0 4 my ( $badflag, $badcode, $xsargs, $parobjs, $optypes, $symtab, $name ) = @_;
2126 1 50       4 return '' unless $bvalflag;
2127              
2128 1 50       3 return PDL::PP::pp_line_numbers(__LINE__, $badcode) if defined $badcode;
2129              
2130 1         4 my $sname = $symtab->get_symname('_PDL_ThisTrans');
2131              
2132 1         3 my @args = map { $_->[0] } @$xsargs;
  1         3  
2133             my %out = map {
2134 1         3 $_ =>
2135             exists($$parobjs{$_}) && exists($$parobjs{$_}{FlagOut})
2136             && !exists($$parobjs{$_}{FlagCreateAlways})
2137 1   33     9 } @args;
2138             my %outca = map {
2139 1         2 $_ =>
2140             exists($$parobjs{$_}) && exists($$parobjs{$_}{FlagOut})
2141             && exists($$parobjs{$_}{FlagCreateAlways})
2142 1   33     7 } @args;
2143             my %tmp = map {
2144 1         2 $_ =>
2145             exists($$parobjs{$_}) && exists($$parobjs{$_}{FlagTemp})
2146 1   33     17 } @args;
2147 1         3 my %other = map { $_ => exists($$optypes{$_}) } @args;
  1         4  
2148              
2149 1         2 my $clear_bad = clear_badflag();
2150 1         2 my $set_bad = set_badflag();
2151 1         3 my $get_bad = get_badflag();
2152              
2153 1         2 my $str = $clear_bad;
2154              
2155             # set the badflag_cache variable if any input piddle has the bad flag set
2156             #
2157 1         2 my $add = 0;
2158 1         2 my $badflag_str = " \$BADFLAGCACHE() = ";
2159 1         3 foreach my $i ( 0 .. $#args ) {
2160 1         2 my $x = $args[$i];
2161 1 50 33     20 unless ( $other{$x} or $out{$x} or $tmp{$x} or $outca{$x}) {
      33        
      33        
2162 1 50       4 if ($add) { $badflag_str .= " || "; }
  0         0  
2163 1         2 else { $add = 1; }
2164 1         4 $badflag_str .= get_badstate($args[$i]);
2165             }
2166             }
2167              
2168             # It is possible, at present, for $add to be 0. I think this is when
2169             # the routine has no input piddles, such as fibonacci in primitive.pd,
2170             # but there may be other cases. These routines could/should (?)
2171             # be marked as NoBadCode to avoid this, or maybe the code here made
2172             # smarter. Left as is for now as do not want to add instability into
2173             # the 2.4.3 release if I can help it - DJB 23 Jul 2006
2174             #
2175 1 50       4 if ($add != 0) {
2176 1         5 $str .= $badflag_str . ";\n if (\$BADFLAGCACHE()) ${set_bad}\n";
2177             } else {
2178 0 0       0 print "\nNOTE: $name has no input bad piddles.\n\n" if $::PP_VERBOSE;
2179             }
2180              
2181 1 50 33     4 if ( defined($badflag) and $badflag == 0 ) {
2182 0         0 $str .=
2183             " if ( $get_bad ) {
2184             printf(\"WARNING: $name does not handle bad values.\\n\");
2185             $clear_bad
2186             }\n";
2187 0 0       0 print "\nNOTE: $name does not handle bad values.\n\n" if $::PP_VERBOSE;
2188             } # if: $badflag
2189              
2190 1         2 PDL::PP::pp_line_numbers(__LINE__, $str)
2191              
2192             } # sub: findbadstatus
2193              
2194              
2195             # copies over the bad value state to the output piddles
2196             #
2197             # if CopyBadStatusCode is set, use it,
2198             # otherwise create the code automatically.
2199             #
2200             # note: this is executed before the trans_mutual call
2201             # is made, since the state may be changed by the
2202             # Code section
2203             #
2204             sub copybadstatus {
2205 1     1 0 4 my ( $badflag, $badcode, $xsargs, $parobjs, $symtab ) = @_;
2206             ## return '' unless $bvalflag or $badflag == 0;
2207 1 50       3 return '' unless $bvalflag;
2208              
2209 1 50       3 if (defined $badcode) {
2210             # realised in 2.4.3 testing that use of $PRIV at this stage is
2211             # dangerous since it may have been freed. So I introduced the
2212             # $BFLACACHE variable which stores the $PRIV(bvalflag) value
2213             # for use here.
2214             # For now make the substitution automatic but it will likely become an
2215             # error to use $PRIV(bvalflag) here.
2216             #
2217 0 0       0 if ($badcode =~ m/\$PRIV(bvalflag)/) {
2218 0         0 $badcode =~ s/\$PRIV(bvalflag)/\$BADFLAGCACHE()/;
2219 0         0 print "\nPDL::PP WARNING: copybadstatus contains '\$PRIV(bvalflag)'; replace with \$BADFLAGCACHE()\n\n";
2220             }
2221 0         0 return PDL::PP::pp_line_numbers(__LINE__, $badcode);
2222             }
2223              
2224             # names of output variables (in calling order)
2225 1         2 my @outs;
2226              
2227             # beware of existance tests like this: $$parobjs{$arg->[0]}{FlagOut} !
2228             # this will cause $$parobjs{$arg->[0]} to spring into existance even if $$parobjs{$arg->[0]}{FlagOut}
2229             # does not exist!!
2230 1         2 foreach my $arg (@$xsargs) {
2231 1         14 my $x = $arg->[0];
2232 1 50 33     21 push (@outs, $x) if (exists ($$parobjs{$x}) and exists ($$parobjs{$x}{FlagOut}));
2233             }
2234              
2235 1         5 my $sname = $symtab->get_symname('_PDL_ThisTrans');
2236 1         2 my $str = '';
2237              
2238             # It appears that some code in Bad.xs sets the cache value but then
2239             # this bit of code never gets called. Is this an efficiency issue (ie
2240             # should we try and optimise away those ocurrences) or does it perform
2241             # some purpose?
2242             #
2243 1         2 $str = "if (\$BADFLAGCACHE()) {\n";
2244 1         2 foreach my $arg ( @outs ) {
2245 0         0 $str .= " " . set_badstate($arg) . ";\n";
2246             }
2247 1         3 $str .= "}\n";
2248              
2249 1         3 PDL::PP::pp_line_numbers(__LINE__, $str);
2250              
2251             } # sub: copybadstatus()
2252              
2253             # insert code, after the autogenerated xs argument processing code
2254             # produced by VarArgsXSHdr and AFTER any in HdrCode
2255             # - this code flags the routine as working inplace,
2256             #
2257             # Inplace can be supplied several values
2258             # => 1
2259             # assumes fn has an inout and output piddle (eg 'a(); [o] b();')
2260             #
2261             # => [ 'a' ]
2262             # assumes several input piddles in sig, so 'a' labels which
2263             # one is to be marked inplace
2264             #
2265             # => [ 'a', 'b' ]
2266             # input piddle is a(), output pidle is 'b'
2267             #
2268             sub InplaceCode {
2269 2     2 0 7 my ( $ppname, $xsargs, $parobjs, $arg ) = @_;
2270 2 50       20 return '' unless defined $arg;
2271              
2272             # find input and output piddles
2273 0         0 my ( @in, @out );
2274 0         0 foreach my $arg (@$xsargs) {
2275 0         0 my $name = $arg->[0];
2276 0 0       0 if ( exists $$parobjs{$name} ) {
2277 0 0       0 if ( exists $$parobjs{$name}{FlagOut} ) {
    0          
2278 0         0 push @out, $name;
2279             } elsif ( ! exists $$parobjs{$name}{FlagTemp} ) {
2280 0         0 push @in, $name;
2281             }
2282             }
2283             }
2284              
2285             # handle different values of arg
2286 0         0 my ( $in, $out );
2287              
2288             # default vals - only set if we have one input/output piddle
2289 0 0       0 $in = $in[0] if $#in == 0;
2290 0 0       0 $out = $out[0] if $#out == 0;
2291              
2292 0 0       0 if ( ref($arg) eq "ARRAY" ) {
    0          
2293 0         0 my $narg = $#$arg;
2294 0 0       0 if ( $narg > -1 ) {
2295 0         0 $in = $$arg[0];
2296 0 0       0 $out = $$arg[1] if $narg > 0;
2297             }
2298             } elsif ( ref($arg) eq "" ) {
2299 0 0       0 return '' unless $arg;
2300             # use default values
2301             } else {
2302 0         0 die "ERROR: Inplace rule [$ppname] must be sent either an array ref or a scalar.\n";
2303             }
2304              
2305 0 0       0 die "ERROR: Inplace [$ppname] does not know name of input piddle\n"
2306             unless defined $in;
2307 0 0       0 die "ERROR: Inplace [$ppname] does not know name of output piddle\n"
2308             unless defined $out;
2309              
2310 0         0 my $instate = $in . "->state";
2311 0         0 PDL::PP::pp_line_numbers(__LINE__,
2312             qq{\tif ( $instate & PDL_INPLACE && ($out != $in)) {
2313             $instate &= ~PDL_INPLACE; PDL_COMMENT("unset")
2314             $out = $in; PDL_COMMENT("discard output value, leak ?")
2315             PDL->SetSV_PDL(${out}_SV,${out});
2316             }})
2317              
2318             } # sub: InplaceCode
2319              
2320             # If there is an EquivCPOffsCode and:
2321             # no bad-value support ==> use that
2322             # bad value support ==> write a bit of code that does
2323             # if ( $PRIV(bvalflag) ) { bad-EquivCPOffsCode }
2324             # else { good-EquivCPOffsCode }
2325             #
2326             # Note: since EquivCPOffsCode doesn't (or I haven't seen any that
2327             # do) use 'loop %{' or 'threadloop %{', we can't rely on
2328             # PDLCode to automatically write code like above, hence the
2329             # explicit definition here.
2330             #
2331             # Note: I *assume* that bad-Equiv..Code == good-Equiv..Code *EXCEPT*
2332             # that we re-define the meaning of the $EQUIVCPOFFS macro to
2333             # check for bad values when copying things over.
2334             # This means having to write less code.
2335             #
2336             # Since PARENT & CHILD need NOT be the same type we cannot just copy
2337             # values from one to the other - we have to check for the presence
2338             # of bad values, hence the expansion for the $bad code
2339             #
2340             # Some operators (notably range) also have an out-of-range flag; they use
2341             # the macro EQUIVCPTRUNC instead of EQUIVCPOFFS.
2342             # $EQUIVCPTRUNC does the same as EQUIVCPOFFS but accepts a child-out-of-bounds
2343             # flag. If the out-of-bounds flag is set, the forward code puts BAD/0 into
2344             # the child, and reverse code refrains from copying.
2345             # --CED 27-Jan-2003
2346             #
2347             # sent [EquivCPOffsCode,BadFlag]
2348              
2349             #
2350             # NOTE: EQUIVCPOFFS and EQUIVCPTRUNC both suffer from the macro-block
2351             # wart of C preprocessing. They look like statements but sometimes
2352             # process into blocks, so if/then/else constructs can get broken.
2353             # Either (1) use blocks for if/then/else, or (2) get excited and
2354             # use the "do {BLOCK} while(0)" block-to-statement conversion construct
2355             # in the substitution. I'm too Lazy. --CED 27-Jan-2003
2356             #
2357             sub CodefromEquivCPOffsCode {
2358 0     0 0 0 my $good = shift;
2359 0         0 my $bflag = shift;
2360              
2361 0         0 my $bad = $good;
2362              
2363             # parse 'good' code
2364 0         0 $good =~ s/\$EQUIVCPOFFS\(([^()]+),([^()]+)\)/\$PP(CHILD)[$1] = \$PP(PARENT)[$2]/g;
2365 0         0 $good =~ s/\$EQUIVCPTRUNC\(([^()]+),([^()]+),([^()]+)\)/\$PP(CHILD)[$1] = ($3) ? 0 : \$PP(PARENT)[$2]/g;
2366              
2367 0         0 my $str = $good;
2368              
2369 0 0 0     0 if ( defined $bflag and $bflag ) {
2370             # parse 'bad' code
2371 0         0 $bad =~ s/\$EQUIVCPOFFS\(([^()]+),([^()]+)\)/if( \$PPISBAD(PARENT,[$2]) ) { \$PPSETBAD(CHILD,[$1]); } else { \$PP(CHILD)[$1] = \$PP(PARENT)[$2]; }/g;
2372 0         0 $bad =~ s/\$EQUIVCPTRUNC\(([^()]+),([^()]+),([^()]+)\)/ if( ($3) || \$PPISBAD(PARENT,[$2]) ) { \$PPSETBAD(CHILD,[$1]); } else {\$PP(CHILD)[$1] = \$PP(PARENT)[$2]; }/g;
2373              
2374 0         0 $str = 'if( $PRIV(bvalflag) ) { ' . $bad . ' } else { ' . $good . '}';
2375             }
2376              
2377 0         0 PDL::PP::pp_line_numbers(__LINE__, $str);
2378              
2379             } # sub: CodefromEquivCPOffsCode
2380              
2381             # this just reverses PARENT & CHILD in the expansion of
2382             # the $EQUIVCPOFFS macro (ie compared to CodefromEquivCPOffsCode)
2383             #
2384             sub BackCodefromEquivCPOffsCode {
2385 0     0 0 0 my $good = shift;
2386 0         0 my $bflag = shift;
2387              
2388 0         0 my $bad = $good;
2389              
2390             # parse 'good' code
2391 0         0 $good =~ s/\$EQUIVCPOFFS\(([^()]+),([^()]+)\)/\$PP(PARENT)[$2] = \$PP(CHILD)[$1]/g;
2392 0         0 $good =~ s/\$EQUIVCPTRUNC\(([^()]+),([^()]+),([^()]+)\)/if(!($3)) \$PP(PARENT)[$2] = \$PP(CHILD)[$1] /g;
2393              
2394 0         0 my $str = $good;
2395              
2396 0 0 0     0 if ( defined $bflag and $bflag ) {
2397             # parse 'bad' code
2398 0         0 $bad =~ s/\$EQUIVCPOFFS\(([^()]+),([^()]+)\)/if( \$PPISBAD(CHILD,[$1]) ) { \$PPSETBAD(PARENT,[$2]); } else { \$PP(PARENT)[$2] = \$PP(CHILD)[$1]; }/g;
2399 0         0 $bad =~ s/\$EQUIVCPTRUNC\(([^()]+),([^()]+),([^()]+)\)/if(!($3)) { if( \$PPISBAD(CHILD,[$1]) ) { \$PPSETBAD(PARENT,[$2]); } else { \$PP(PARENT)[$2] = \$PP(CHILD)[$1]; } } /g;
2400              
2401 0         0 $str = 'if ( $PRIV(bvalflag) ) { ' . $bad . ' } else { ' . $good . '}';
2402             }
2403              
2404 0         0 PDL::PP::pp_line_numbers(__LINE__, $str);
2405              
2406             } # sub: BackCodefromEquivCPOffsCode
2407              
2408             sub GenDocs {
2409 2     2 0 9 my ($name,$pars,$otherpars,$doc,$baddoc) = @_;
2410              
2411             # Allow explcit non-doc using Doc=>undef
2412              
2413 2 0 33     11 return '' if $doc eq '' && (!defined $doc) && $doc==undef;
      33        
2414 2 50       14 return '' if $doc =~ /^\s*internal\s*$/i;
2415              
2416             # remove any 'bad' documentation if we're not compiling support
2417 2 50       7 $baddoc = undef unless $bvalflag;
2418              
2419             # If the doc string is one line let's have to for the
2420             # reference card information as well
2421 2         4 my @splitRes; # temp split variable to get rid of
2422             # 'implicit split to @_ is deprecated' messages
2423 2 50       13 $doc = "=for ref\n\n".$doc if( scalar(@splitRes = split("\n", $doc)) <= 1);
2424              
2425 2         6 $::DOCUMENTED++;
2426 2 50       7 $pars = "P(); C()" unless $pars;
2427             # Strip leading whitespace and trailing semicolons and whitespace
2428 2         13 $pars =~ s/^\s*(.+[^;])[;\s]*$/$1/;
2429 2 50       8 $otherpars =~ s/^\s*(.+[^;])[;\s]*$/$1/ if $otherpars;
2430 2 50       10 my $sig = "$pars".( $otherpars ? "; $otherpars" : "");
2431              
2432 2         7 $doc =~ s/\n(=cut\s*\n)+(\s*\n)*$/\n/m; # Strip extra =cut's
2433 2 50       24 if ( defined $baddoc ) {
2434             # Strip leading newlines and any =cut markings
2435 2         18 $baddoc =~ s/\n(=cut\s*\n)+(\s*\n)*$/\n/m;
2436 2         6 $baddoc =~ s/^\n+//;
2437 2         8 $baddoc = "=for bad\n\n$baddoc";
2438             }
2439              
2440 2         12 my $baddoc_function_pod = <<"EOD" ;
2441              
2442             XXX=head2 $name
2443              
2444             XXX=for sig
2445              
2446             Signature: ($sig)
2447              
2448             $doc
2449              
2450             $baddoc
2451              
2452             XXX=cut
2453              
2454             EOD
2455              
2456 2         14 $baddoc_function_pod =~ s/^XXX=/=/gms;
2457 2         9 return $baddoc_function_pod;
2458             }
2459              
2460             sub ToIsReversible {
2461 0     0 0 0 my($rev) = @_;
2462 0 0       0 if($rev eq "1") {
2463 0         0 PDL::PP::pp_line_numbers(__LINE__, '$SETREVERSIBLE(1)')
2464             } else {
2465 0         0 PDL::PP::pp_line_numbers(__LINE__, $rev)
2466             }
2467             }
2468              
2469             sub make_newcoerce {
2470 0     0 0 0 my($ftypes) = @_;
2471             PDL::PP::pp_line_numbers(__LINE__, join '',map {
2472 0         0 "$_->datatype = $ftypes->{$_}; "
  0         0  
2473             } keys %$ftypes);
2474             }
2475              
2476             # Assuming that, if HASP2Child is true, we only have
2477             # PARENT; CHILD parameters, so we can just take the
2478             # datatype to be that of PARENT (which is set up by
2479             # find_datatype()). Little bit complicated because
2480             # we need to set CHILD's datatype under certain
2481             # circumstances
2482             #
2483             sub coerce_types {
2484 2     2 0 6 my($parnames,$parobjs,$ignore,$newstab,$hasp2child) = @_;
2485              
2486             # assume [oca]CHILD();, although there might be an ignore
2487 2 50       8 if ( $hasp2child ) {
2488 0         0 my $child = $$parnames[1];
2489 0 0       0 return "" if $ignore->{$child};
2490              
2491             die "ERROR: expected $child to be [oca]\n"
2492 0 0       0 unless $parobjs->{$child}{FlagCreateAlways};
2493              
2494 0         0 return PDL::PP::pp_line_numbers(__LINE__, "$child\->datatype = \$PRIV(__datatype);\n$child\->has_badvalue = \$PRIV(has_badvalue);\n$child\->badvalue = \$PRIV(badvalue);\n");
2495             }
2496              
2497 2         4 my $str = "";
2498 2         7 foreach ( @$parnames ) {
2499 2 50       6 next if $ignore->{$_};
2500              
2501 2         6 my $po = $parobjs->{$_};
2502              
2503 2         3 my $dtype;
2504 2 50       16 if ( $po->{FlagTyped} ) {
2505 0         0 $dtype = $po->cenum();
2506             $dtype = "PDLMAX($dtype,\$PRIV(__datatype))"
2507 0 0       0 if $po->{FlagTplus};
2508             } else {
2509 2         6 $dtype = "\$PRIV(__datatype)";
2510             }
2511              
2512 2 50       7 if ( $po->{FlagCreateAlways} ) {
2513 0         0 $str .= "$_->datatype = $dtype; ";
2514             } else {
2515             $str .=
2516             "if( ($_->state & PDL_NOMYDIMS) && $_->trans == NULL ) {
2517             $_->datatype = $dtype;
2518             } else "
2519 2 50       7 if $po->{FlagCreat};
2520 2         24 $str .= "if($dtype != $_->datatype) {
2521             $_ = PDL->get_convertedpdl($_,$dtype);
2522             }";
2523             }
2524             } # foreach: @$parnames
2525              
2526 2         7 PDL::PP::pp_line_numbers(__LINE__, $str);
2527             } # sub: coerce_types()
2528              
2529             # First, finds the greatest datatype, then, if not supported, takes
2530             # the largest type supported by the function.
2531             # Not yet optimal.
2532             #
2533             # Assuming that, if HASP2Child is true, we only have
2534             # PARENT; CHILD parameters, so we can just take the
2535             # datatype to be that of PARENT (see also coerce_types())
2536             #
2537             sub find_datatype {
2538 2     2 0 7 my($parnames,$parobjs,$ignore,$newstab,$gentypes,$hasp2child) = @_;
2539              
2540 2         5 my $dtype = "\$PRIV(__datatype)";
2541              
2542             # TODO XXX
2543             # the check can probably be removed, but left in since I don't know
2544             # what I'm doing (DJB)
2545 2 50 33     9 die "ERROR: gentypes != $ntypes with p2child\n"
2546             if $hasp2child and $#$gentypes != $ntypes;
2547              
2548 2 50       5 return "$dtype = $$parnames[0]\->datatype;\n\$PRIV(has_badvalue) = $$parnames[0]\->has_badvalue;\n\$PRIV(badvalue) = $$parnames[0]\->badvalue;\n"
2549             if $hasp2child;
2550              
2551 2         6 my $str = "$dtype = 0;";
2552 2         5 foreach ( @$parnames ) {
2553 2         4 my $po = $parobjs->{$_};
2554 2 50 33     28 next if $ignore->{$_} or $po->{FlagTyped} or $po->{FlagCreateAlways};
      33        
2555              
2556 2         6 $str .= "if(";
2557             $str .= "!(($_->state & PDL_NOMYDIMS) &&
2558             $_->trans == NULL) && "
2559 2 50       7 if $po->{FlagCreat};
2560 2         10 $str .= "$dtype < $_->datatype) {
2561             $dtype = $_->datatype;
2562             }\n";
2563             } # foreach: @$parnames
2564              
2565 2         7 $str .= join '', map { "if($dtype == PDL_$_) {}\nelse " }(@$gentypes);
  12         45  
2566              
2567 2         20 PDL::PP::pp_line_numbers(__LINE__, $str .= "$dtype = PDL_$gentypes->[-1];\n");
2568             } # sub: find_datatype()
2569              
2570 1     1 0 3 sub NT2Decls_p {&NT2Decls__({ToPtrs=>1},@_);}
2571              
2572 1     1 0 4 sub NT2Copies_p {&NT2Copies__({ToPtrs=>1},@_);}
2573              
2574 2     2 0 6 sub NT2Free_p {&NT2Free__({ToPtrs=>1},@_);}
2575              
2576 1     1 0 3 sub NT2Decls {&NT2Decls__({},@_);}
2577              
2578             sub NT2Decls__ {
2579 2     2 0 5 my($opts,$onames,$otypes) = @_;
2580 2         4 my $decl;
2581 2         2 my $dopts = {};
2582 2 100       6 $dopts->{VarArrays2Ptrs} = 1 if $opts->{ToPtrs};
2583 2         4 for(@$onames) {
2584 3         8 $decl .= $otypes->{$_}->get_decl($_,$dopts).";";
2585             }
2586 2         5 PDL::PP::pp_line_numbers(__LINE__, $decl);
2587             }
2588              
2589             sub NT2Copies__ {
2590 1     1 0 3 my($opts,$onames,$otypes,$copyname) = @_;
2591 1         2 my $decl;
2592 1         2 my $dopts = {};
2593 1 50       4 $dopts->{VarArrays2Ptrs} = 1 if $opts->{ToPtrs};
2594 1         2 for(@$onames) {
2595 0         0 $decl .= $otypes->{$_}->get_copy("\$PRIV($_)","$copyname->$_",
2596             $dopts).";";
2597             }
2598 1         2 PDL::PP::pp_line_numbers(__LINE__, $decl);
2599             }
2600              
2601             sub NT2Free__ {
2602 2     2 0 5 my($opts,$onames,$otypes) = @_;
2603 2         2 my $decl;
2604 2         4 my $dopts = {};
2605 2 50       6 $dopts->{VarArrays2Ptrs} = 1 if $opts->{ToPtrs};
2606 2         4 for(@$onames) {
2607 3         10 $decl .= $otypes->{$_}->get_free("\$PRIV($_)",
2608             $dopts).";";
2609             }
2610 2         4 PDL::PP::pp_line_numbers(__LINE__, $decl);
2611             }
2612              
2613             # The undef is just so that PrivIsInc gets set. Is this really
2614             # needed (well, it is since the rule fails if there aren't 2
2615             # return values; what I meant is what does PrivIsInc do for
2616             # us?)
2617             #
2618             sub make_incsizes {
2619 1     1 0 6 my($parnames,$parobjs,$dimobjs,$havethreading) = @_;
2620             my $str = ($havethreading?"pdl_thread __pdlthread; ":"").
2621 1         5 (join '',map {$parobjs->{$_}->get_incdecls} @$parnames).
2622 1 50       7 (join '',map {$_->get_decldim} sort values %$dimobjs);
  1         4  
2623 1         3 return ($str,undef);
2624             }
2625              
2626             sub make_incsize_copy {
2627 1     1 0 3 my($parnames,$parobjs,$dimobjs,$copyname,$havethreading) = @_;
2628             PDL::PP::pp_line_numbers(__LINE__,
2629             ($havethreading?
2630             "PDL->thread_copy(&(\$PRIV(__pdlthread)),&($copyname->__pdlthread));"
2631             : "").
2632 1     1   5 (join '',map {$parobjs->{$_}->get_incdecl_copy(sub{"\$PRIV($_[0])"},
2633 1     1   22 sub{"$copyname->$_[0]"})} @$parnames).
  1         6  
2634 1 50   1   6 (join '',map {$_->get_copydim(sub{"\$PRIV($_[0])"},
  1         8  
2635 1     1   7 sub{"$copyname->$_[0]"})} sort values %$dimobjs)
  1         4  
2636             );
2637              
2638             }
2639              
2640             sub make_incsize_free {
2641 1     1 0 3 my($parnames,$parobjs,$dimobjs,$havethreading) = @_;
2642 1 50       16 $havethreading ?
2643             PDL::PP::pp_line_numbers(__LINE__, 'PDL->freethreadloop(&($PRIV(__pdlthread)));')
2644             : ''
2645             }
2646              
2647             sub make_parnames {
2648 1     1 0 2 my($pnames,$pobjs,$dobjs) = @_;
2649 1         3 my @pdls = map {$pobjs->{$_}} @$pnames;
  1         4  
2650 1         3 my $npdls = $#pdls+1;
2651 1         3 my $join__parnames = join ",",map {qq|"$_"|} @$pnames;
  1         4  
2652 1         3 my $join__realdims = join ",",map {$#{$_->{IndObjs}}+1} @pdls;
  1         1  
  1         4  
2653 1 50       15 if($Config{cc} eq 'cl') {
2654 0 0       0 $join__parnames = '""' if $join__parnames eq '';
2655 0 0       0 $join__realdims = '0' if $join__realdims eq '';
2656             }
2657 1         8 PDL::PP::pp_line_numbers(__LINE__, "static char *__parnames[] = {". $join__parnames ."};
2658             static PDL_Indx __realdims[] = {". $join__realdims . "};
2659             static char __funcname[] = \"\$MODULE()::\$NAME()\";
2660             static pdl_errorinfo __einfo = {
2661             __funcname, __parnames, $npdls
2662             };
2663             ");
2664             }
2665              
2666             ##############################
2667             #
2668             # hdrcheck -- examine the various PDLs that form the output PDL,
2669             # and copy headers as necessary. The last header found with the hdrcpy
2670             # bit set is used. This used to do just a simple ref copy but now
2671             # it uses the perl routine PDL::_hdr_copy to do the dirty work. That
2672             # routine makes a deep copy of the header. Copies of the deep copy
2673             # are distributed to all the names of the piddle that are not the source
2674             # of the header. I believe that is the Right Thing to do but I could be
2675             # wrong.
2676             #
2677             # It's hard to read this sort of macro stuff so here's the flow:
2678             # - Check the hdrcpy flag. If it's set, then check the header
2679             # to see if it exists. If it doees, we need to call the
2680             # perl-land PDL::_hdr_copy routine. There are some shenanigans
2681             # to keep the return value from evaporating before we've had a
2682             # chance to do our bit with it.
2683             # - For each output argument in the function signature, try to put
2684             # a reference to the new header into that argument's header slot.
2685             # (For functions with multiple outputs, this produces multiple linked
2686             # headers -- that could be Wrong; fixing it would require making
2687             # yet more explicit copies!)
2688             # - Remortalize the return value from PDL::_hdr_copy, so that we don't
2689             # leak memory.
2690             #
2691             # --CED 12-Apr-2003
2692             #
2693              
2694             sub hdrcheck {
2695 1     1 0 3 my ($pnames,$pobjs) = @_;
2696              
2697 1         2 my $nn = $#$pnames;
2698 1         3 my @names = map { "\$PRIV(pdls[$_])" } 0..$nn;
  1         3  
2699              
2700             # from make_redodims_thread() we know that __creating[] == 0 unless
2701             # ...{FlagCreat} is true
2702             #
2703 1         2 my $str = "
2704             { PDL_COMMENT(\"convenience block\")
2705             void *hdrp = NULL;
2706             char propagate_hdrcpy = 0;
2707             SV *hdr_copy = NULL;
2708             ";
2709              
2710             # Find a header among the possible names
2711 1         4 foreach ( 0 .. $nn ) {
2712 1 50       4 my $aux = $pobjs->{$pnames->[$_]}{FlagCreat} ? "!__creating[$_] && \n" : "";
2713 1         7 $str .= <<"HdRCHECK1"
2714             if(!hdrp &&
2715             $aux $names[$_]\->hdrsv &&
2716             ($names[$_]\->state & PDL_HDRCPY)
2717             ) {
2718             hdrp = $names[$_]\->hdrsv;
2719             propagate_hdrcpy = (($names[$_]\->state & PDL_HDRCPY) != 0);
2720             }
2721             HdRCHECK1
2722             ;
2723             }
2724              
2725             $str .= << 'DeePcOPY'
2726             if (hdrp) {
2727             if(hdrp == &PL_sv_undef)
2728             hdr_copy = &PL_sv_undef;
2729             else { PDL_COMMENT("Call the perl routine _hdr_copy...")
2730             int count;
2731             PDL_COMMENT("Call the perl routine PDL::_hdr_copy(hdrp)")
2732             dSP;
2733             ENTER ;
2734             SAVETMPS ;
2735             PUSHMARK(SP) ;
2736             XPUSHs( hdrp );
2737             PUTBACK ;
2738             count = call_pv("PDL::_hdr_copy",G_SCALAR);
2739             SPAGAIN ;
2740             if(count != 1)
2741             croak("PDL::_hdr_copy didn't return a single value - please report this bug (A).");
2742              
2743             hdr_copy = (SV *)POPs;
2744              
2745             if(hdr_copy && hdr_copy != &PL_sv_undef) {
2746             (void)SvREFCNT_inc(hdr_copy); PDL_COMMENT("Keep hdr_copy from vanishing during FREETMPS")
2747             }
2748              
2749             FREETMPS ;
2750             LEAVE ;
2751              
2752              
2753             } PDL_COMMENT("end of callback block")
2754              
2755             DeePcOPY
2756 1         3 ;
2757             # if(hdrp) block is still open -- now reassign all the aliases...
2758              
2759             # Found the header -- now copy it into all the right places.
2760 1         3 foreach ( 0 .. $nn ) {
2761             $str .= <<"HdRCHECK2"
2762             if ( $names[$_]\->hdrsv != hdrp ){
2763             if( $names[$_]\->hdrsv && $names[$_]\->hdrsv != &PL_sv_undef)
2764             (void)SvREFCNT_dec( $names[$_]\->hdrsv );
2765             if( hdr_copy != &PL_sv_undef )
2766             (void)SvREFCNT_inc(hdr_copy);
2767             $names[$_]\->hdrsv = hdr_copy;
2768             }
2769             if(propagate_hdrcpy)
2770             $names[$_]\->state |= PDL_HDRCPY;
2771             HdRCHECK2
2772              
2773             # QUESTION: what is the following line doing?
2774             #
2775 1 50       4 if ( $pobjs->{$pnames->[$_]}{FlagCreat} );
2776             }
2777              
2778 1         2 $str .= '
2779             if(hdr_copy != &PL_sv_undef)
2780             SvREFCNT_dec(hdr_copy); PDL_COMMENT("make hdr_copy mortal again")
2781             } PDL_COMMENT("end of if(hdrp) block")
2782             } PDL_COMMENT("end of conv. block")
2783             ';
2784 1         3 PDL::PP::pp_line_numbers(__LINE__, $str);
2785              
2786             } # sub: hdrcheck()
2787              
2788             sub make_redodims_thread {
2789             #my($pnames,$pobjs,$dobjs,$dpars,$pcode ) = @_;
2790 1     1 0 4 my($pnames,$pobjs,$dobjs,$dpars,$pcode, $noPthreadFlag) = @_;
2791 1         2 my $str = PDL::PP::pp_line_numbers(__LINE__, '');
2792 1         2 my $npdls = @$pnames;
2793              
2794 1 50       5 $noPthreadFlag = 0 unless( defined $noPthreadFlag ); # assume we can pthread, unless indicated otherwise
2795              
2796 1         2 my $nn = $#$pnames;
2797 1         3 my @privname = map { "\$PRIV(pdls[$_])" } ( 0 .. $nn );
  1         4  
2798 1 50       5 $str .= $npdls ? "PDL_Indx __creating[$npdls];\n" : "PDL_Indx __creating[1];\n";
2799 1         5 $str .= join '',map {$_->get_initdim."\n"} sort values %$dobjs;
  1         3  
2800              
2801             # if FlagCreat is NOT true, then we set __creating[] to 0
2802             # and we can use this knowledge below, and in hdrcheck()
2803             # and in PP/PdlParObj (get_xsnormdimchecks())
2804             #
2805 1         4 foreach ( 0 .. $nn ) {
2806 1         4 $str .= "__creating[$_] = ";
2807 1 50       6 if ( $pobjs->{$pnames->[$_]}{FlagCreat} ) {
2808 0         0 $str .= "PDL_CR_SETDIMSCOND(__privtrans,$privname[$_]);\n";
2809             } else {
2810 1         2 $str .= "0;\n";
2811             }
2812             } # foreach: 0 .. $nn
2813              
2814 1         15 $str .= " {\n$pcode\n}\n";
2815 1         6 $str .= " {\n " . make_parnames($pnames,$pobjs,$dobjs) . "
2816             PDL->initthreadstruct(2,\$PRIV(pdls),
2817             __realdims,__creating,$npdls,
2818             &__einfo,&(\$PRIV(__pdlthread)),
2819             \$PRIV(vtable->per_pdl_flags),
2820             $noPthreadFlag );
2821             }\n";
2822 1         4 $str .= join '',map {$pobjs->{$_}->get_xsnormdimchecks()} @$pnames;
  1         5  
2823 1         6 $str .= hdrcheck($pnames,$pobjs);
2824 1         6 $str .= join '',map {$pobjs->{$pnames->[$_]}->
  1         5  
2825             get_incsets($privname[$_])} 0..$nn;
2826 1         14 return $str;
2827              
2828             } # sub: make_redodims_thread()
2829              
2830             sub XSHdr {
2831 2     2 0 6 my($xsname,$nxargs) = @_;
2832 2         10 return XS::mkproto($xsname,$nxargs);
2833             }
2834              
2835             ###########################################################
2836             # Name : extract_signature_from_fulldoc
2837             # Usage : $sig = extract_signature_from_fulldoc($fulldoc)
2838             # Purpose : pull out the signature from the fulldoc string
2839             # Returns : whatever is in parentheses in the signature, or undef
2840             # Parameters : $fulldoc
2841             # Throws : never
2842             # Notes : the signature must have the following form:
2843             # :
2844             # : =for sig
2845             # :
2846             # : Signature: (
2847             # : be multiline>)
2848             # :
2849             # :
2850             # : The two spaces before "Signature" are required, as are
2851             # : the parentheses.
2852             sub extract_signature_from_fulldoc {
2853 0     0 0 0 my $fulldoc = shift;
2854 0 0       0 if ($fulldoc =~ /=for sig\n\n Signature: \(([^\n]*)\n/g) {
2855             # Extract the signature and remove the final parenthesis
2856 0         0 my $sig = $1;
2857 0         0 $sig .= $1 while $fulldoc =~ /\G\h+([^\n]*)\n/g;
2858 0         0 $sig =~ s/\)\s*$//;
2859 0         0 return $sig;
2860             }
2861 0         0 return;
2862             }
2863              
2864              
2865             # Build the valid-types regex and valid Pars argument only once. These are
2866             # also used in PDL::PP::PdlParObj, which is why they are globally available.
2867 2     2   44 use PDL::PP::PdlParObj;
  2         5  
  2         7572  
2868             my $pars_re = $PDL::PP::PdlParObj::pars_re;
2869              
2870             ###########################################################
2871             # Name : build_pars_from_fulldoc
2872             # Usage : $pars = build_pars_from_fulldoc($fulldoc)
2873             # Purpose : extract the Pars from the signature from the fulldoc string,
2874             # : the part of the signature that specifies the piddles
2875             # Returns : a string appropriate for the Pars key
2876             # Parameters : $fulldoc
2877             # Throws : if there is no signature
2878             # : if there is no extractable Pars section
2879             # : if some PDL arguments come after the OtherPars arguments start
2880             # Notes : This is meant to be used directly in a Rule. Therefore, it
2881             # : is only called if the Pars key does not yet exist, so if it
2882             # : is not possible to extract the Pars section, it dies.
2883             sub build_pars_from_fulldoc {
2884 0     0 0 0 my $fulldoc = shift;
2885            
2886             # Get the signature or die
2887 0 0       0 my $sig = extract_signature_from_fulldoc($fulldoc)
2888             or confess('No Pars specified and none could be extracted from FullDoc');
2889            
2890             # Everything is semicolon-delimited
2891 0         0 my @args = split /\s*;\s*/, $sig;
2892 0         0 my @pars;
2893 0         0 my $switched_to_other_pars = 0;
2894 0         0 for my $arg (@args) {
2895 0 0 0     0 confess('All PDL args must come before other pars in FullDoc signature')
2896             if $switched_to_other_pars and $arg =~ $pars_re;
2897 0 0       0 if ($arg =~ $pars_re) {
2898 0         0 push @pars, $arg;
2899             }
2900             else {
2901 0         0 $switched_to_other_pars = 1;
2902             }
2903             }
2904            
2905             # Make sure there's something there
2906 0 0       0 confess('FullDoc signature contains no PDL arguments') if @pars == 0;
2907            
2908             # All done!
2909 0         0 return join('; ', @pars);
2910             }
2911              
2912             ###########################################################
2913             # Name : build_otherpars_from_fulldoc
2914             # Usage : $otherpars = build_otherpars_from_fulldoc($fulldoc)
2915             # Purpose : extract the OtherPars from the signature from the fulldoc
2916             # : string, the part of the signature that specifies non-piddle
2917             # : arguments
2918             # Returns : a string appropriate for the OtherPars key
2919             # Parameters : $fulldoc
2920             # Throws : if some OtherPars arguments come before the last PDL argument
2921             # Notes : This is meant to be used directly in a Rule. Therefore, it
2922             # : is only called if the OtherPars key does not yet exist.
2923             sub build_otherpars_from_fulldoc {
2924 0     0 0 0 my $fulldoc = shift;
2925            
2926             # Get the signature or do not set
2927 0 0       0 my $sig = extract_signature_from_fulldoc($fulldoc)
2928             or return 'DO NOT SET!!';
2929            
2930             # Everything is semicolon-delimited
2931 0         0 my @args = split /\s*;\s*/, $sig;
2932 0         0 my @otherpars;
2933 0         0 for my $arg (@args) {
2934 0 0 0     0 confess('All PDL args must come before other pars in FullDoc signature')
2935             if @otherpars > 0 and $arg =~ $pars_re;
2936 0 0       0 if ($arg !~ $pars_re) {
2937 0         0 push @otherpars, $arg;
2938             }
2939             }
2940            
2941             # All done!
2942 0 0       0 return 'DO NOT SET!!'if @otherpars == 0;
2943 0         0 return join('; ', @otherpars);
2944             }
2945              
2946             # Set up the rules for translating the pp_def contents.
2947             #
2948             $PDL::PP::deftbl =
2949             [
2950             # used as a flag for many of the routines
2951             # ie should we bother with bad values for this routine?
2952             # 1 - yes,
2953             # 0 - no, maybe issue a warning
2954             # undef - we're not compiling with bad value support
2955             #
2956             PDL::PP::Rule->new("BadFlag", "_HandleBad",
2957             "Sets BadFlag based upon HandleBad key and PDL's ability to handle bad values",
2958             sub { return (defined $_[0]) ? ($bvalflag and $_[0]) : undef; }),
2959              
2960             ####################
2961             # FullDoc Handling #
2962             ####################
2963            
2964             # Error processing: does FullDoc contain BadDoc, yet BadDoc specified?
2965             PDL::PP::Rule::Croak->new(['FullDoc', 'BadDoc'],
2966             'Cannot have both FullDoc and BadDoc defined'),
2967             PDL::PP::Rule::Croak->new(['FullDoc', 'Doc'],
2968             'Cannot have both FullDoc and Doc defined'),
2969             # Note: no error processing on Pars; it's OK for the docs to gloss over
2970             # the details.
2971            
2972             # Add the Pars section based on the signature of the FullDoc if the Pars
2973             # section doesn't already exist
2974             PDL::PP::Rule->new('Pars', 'FullDoc',
2975             'Sets the Pars from the FullDoc if Pars is not explicitly specified',
2976             \&build_pars_from_fulldoc
2977             ),
2978             PDL::PP::Rule->new('OtherPars', 'FullDoc',
2979             'Sets the OtherPars from the FullDoc if OtherPars is not explicitly specified',
2980             \&build_otherpars_from_fulldoc
2981             ),
2982            
2983             ################################
2984             # Other Documentation Handling #
2985             ################################
2986            
2987             # no docs by default
2988             PDL::PP::Rule::Returns->new("Doc", [], 'Sets the default doc string',
2989             "\n=for ref\n\ninfo not available\n"),
2990            
2991             # try and automate the docs
2992             # could be really clever and include the sig to see about
2993             # input/output params, for instance
2994            
2995             PDL::PP::Rule->new("BadDoc", ["BadFlag","Name","_CopyBadStatusCode"],
2996             'Sets the default documentation for handling of bad values',
2997             sub {
2998             return undef unless $bvalflag;
2999             my ( $bf, $name, $code ) = @_;
3000             my $str;
3001             if ( not defined($bf) ) {
3002             $str = "$name does not process bad values.\n";
3003             } elsif ( $bf ) {
3004             $str = "$name processes bad values.\n";
3005             } else {
3006             $str = "$name ignores the bad-value flag of the input piddles.\n";
3007             }
3008             if ( not defined($code) ) {
3009             $str .= "It will set the bad-value flag of all output piddles if " .
3010             "the flag is set for any of the input piddles.\n";
3011             } elsif ( $code eq '' ) {
3012             $str .= "The output piddles will NOT have their bad-value flag set.\n";
3013             } else {
3014             $str .= "The state of the bad-value flag of the output piddles is unknown.\n";
3015             }
3016             }
3017             ),
3018              
3019             # Default: no otherpars
3020             PDL::PP::Rule::Returns::EmptyString->new("OtherPars"),
3021              
3022             # the docs
3023             PDL::PP::Rule->new("PdlDoc", "FullDoc", sub {
3024             my $fulldoc = shift;
3025            
3026             # Remove bad documentation if bad values are not supported
3027             $fulldoc =~ s/=for bad\n\n.*?\n\n//s unless $bvalflag;
3028            
3029             # Append a final cut if it doesn't exist due to heredoc shinanigans
3030             $fulldoc .= "\n\n=cut\n" unless $fulldoc =~ /\n=cut\n*$/;
3031            
3032             # Make sure the =head1 FUNCTIONS section gets added
3033             $::DOCUMENTED++;
3034            
3035             return $fulldoc;
3036             }
3037             ),
3038             PDL::PP::Rule->new("PdlDoc", ["Name","_Pars","OtherPars","Doc","_BadDoc"], \&GenDocs),
3039            
3040             ##################
3041             # Done with Docs #
3042             ##################
3043            
3044             # Notes
3045             # Suffix 'NS' means, "Needs Substitution". In other words, the string
3046             # associated with a key that has the suffix "NS" must be run through a
3047             # Substitute or Substitute::Usual
3048              
3049             # some defaults
3050             #
3051             PDL::PP::Rule::Returns->new("CopyName", [],
3052             'Sets the CopyName key to the default: __copy', "__copy"),
3053              
3054             PDL::PP::Rule->new("DefaultFlowCodeNS", "_DefaultFlow",
3055             'Sets the code to handle dataflow flags, if applicable',
3056             sub { pp_line_numbers(__LINE__, $_[0] ?
3057             '$PRIV(flags) |= PDL_ITRANS_DO_DATAFLOW_F | PDL_ITRANS_DO_DATAFLOW_B;'
3058             : 'PDL_COMMENT("No flow")') }),
3059              
3060             # Question: where is ppdefs defined?
3061             # Answer: Core/Types.pm
3062             #
3063             PDL::PP::Rule->new("GenericTypes", [],
3064             'Sets GenericTypes flag to all types known to PDL::Types',
3065             sub {[ppdefs]}),
3066              
3067             PDL::PP::Rule->new("ExtraGenericLoops", "FTypes",
3068             'Makes ExtraGenericLoops identical to FTypes if the latter exists and the former does not',
3069             sub {return $_[0]}),
3070             PDL::PP::Rule::Returns->new("ExtraGenericLoops", [],
3071             'Sets ExtraGenericLoops to an empty hash if it does not already exist', {}),
3072              
3073             PDL::PP::Rule::InsertName->new("StructName", 'pdl_${name}_struct'),
3074             PDL::PP::Rule::InsertName->new("VTableName", 'pdl_${name}_vtable'),
3075              
3076             PDL::PP::Rule->new("FHdrInfo", ["Name","StructName"],
3077             sub { return { Name => $_[0], StructName => $_[1], }; }),
3078              
3079             # Treat exchanges as affines. Affines assumed to be parent->child.
3080             # Exchanges may, if the want, handle threadids as well.
3081             # Same number of dimensions is assumed, though.
3082             #
3083             PDL::PP::Rule->new("AffinePriv", "XCHGOnly", sub { return @_; }),
3084             PDL::PP::Rule::Returns->new("Priv", "AffinePriv", 'PDL_Indx incs[$CHILD(ndims)];PDL_Indx offs; '),
3085             PDL::PP::Rule::Returns->new("IsAffineFlag", "AffinePriv", "PDL_ITRANS_ISAFFINE"),
3086              
3087             PDL::PP::Rule->new("RedoDims", ["EquivPDimExpr","FHdrInfo","_EquivDimCheck"],
3088             \&pdimexpr2priv),
3089             PDL::PP::Rule->new("RedoDims", ["Identity","FHdrInfo"],
3090             \&identity2priv),
3091              
3092             # NOTE: we use the same bit of code for all-good and bad data -
3093             # see the Code rule
3094             #
3095             PDL::PP::Rule->new("EquivCPOffsCode", "Identity",
3096             "something to do with dataflow between CHILD & PARENT, I think.",
3097             \&equivcpoffscode),
3098              
3099             PDL::PP::Rule->new("Code", ["EquivCPOffsCode","BadFlag"],
3100             "create Code from EquivCPOffsCode",
3101             \&CodefromEquivCPOffsCode),
3102              
3103             PDL::PP::Rule->new("BackCode", ["EquivCPOffsCode","BadFlag"],
3104             "create BackCode from EquivCPOffsCode",
3105             \&BackCodefromEquivCPOffsCode),
3106              
3107             PDL::PP::Rule::Returns::Zero->new("Affine_Ok", "EquivCPOffsCode"),
3108             PDL::PP::Rule::Returns::One->new("Affine_Ok"),
3109              
3110             PDL::PP::Rule::Returns::NULL->new("ReadDataFuncName", "AffinePriv"),
3111             PDL::PP::Rule::Returns::NULL->new("WriteBackDataFuncName", "AffinePriv"),
3112              
3113             PDL::PP::Rule::InsertName->new("ReadDataFuncName", 'pdl_${name}_readdata'),
3114             PDL::PP::Rule::InsertName->new("CopyFuncName", 'pdl_${name}_copy'),
3115             PDL::PP::Rule::InsertName->new("FreeFuncName", 'pdl_${name}_free'),
3116             PDL::PP::Rule::InsertName->new("RedoDimsFuncName", 'pdl_${name}_redodims'),
3117              
3118             # There used to be a BootStruct rule which just became copied to the XSBootCode
3119             # rule, so it has been removed.
3120             #
3121             PDL::PP::Rule->new("XSBootCode", ["AffinePriv","VTableName"],
3122             sub {return " $_[1].readdata = PDL->readdata_affine;\n" .
3123             " $_[1].writebackdata = PDL->writebackdata_affine;\n"}),
3124              
3125             # Parameters in the form 'parent and child(this)'.
3126             # The names are PARENT and CHILD.
3127             #
3128             # P2Child implicitly means "no data type changes".
3129              
3130             PDL::PP::Rule->new(["USParNames","USParObjs","FOOFOONoConversion","HaveThreading","NewXSName"],
3131             ["P2Child","Name","BadFlag"],
3132             \&NewParentChildPars),
3133              
3134             PDL::PP::Rule::InsertName->new("NewXSName", '_${name}_int'),
3135              
3136             PDL::PP::Rule::Returns->new("EquivPThreadIdExpr", "P2Child",
3137             '$CTID-$PARENT(ndims)+$CHILD(ndims)'),
3138              
3139             PDL::PP::Rule::Returns::One->new("HaveThreading"),
3140              
3141             # Parameters in the 'a(x,y); [o]b(y)' format, with
3142             # fixed nos of real, unthreaded-over dims.
3143             #
3144             # XXX
3145             # - the need for BadFlag is due to hacked get_xsdatapdecl()
3146             # in PP/PdlParObj and because the PdlParObjs are created by
3147             # PDL::PP::Signature (Doug Burke 07/08/00)
3148              
3149             PDL::PP::Rule->new(["USParNames","USParObjs","DimmedPars"], ["Pars","BadFlag"], \&Pars_nft),
3150             PDL::PP::Rule->new("DimObjs", ["USParNames","USParObjs"], \&ParObjs_DimObjs),
3151              
3152             # Set CallCopy flag for simple functions (2-arg with 0-dim signatures)
3153             # This will copy the $object->copy method, instead of initialize
3154             # for PDL-subclassed objects
3155             #
3156             PDL::PP::Rule->new("CallCopy", ["DimObjs", "USParNames", "USParObjs", "Name", "_P2Child"],
3157             sub {
3158             my ($dimObj, $USParNames, $USParObjs, $Name, $hasp2c) = @_;
3159             return 0 if $hasp2c;
3160             my $noDimmedArgs = scalar(keys %$dimObj);
3161             my $noArgs = scalar(@$USParNames);
3162             if( $noDimmedArgs == 0 and $noArgs == 2 ){
3163             # Check for 2-arg functgion with 0-dim signatures
3164             # Check to see if output arg is _not_ explicitly typed:
3165             my $arg2 = $USParNames->[1];
3166             my $ParObj = $USParObjs->{$arg2};
3167             if( $ParObj->ctype('generic') eq 'generic'){
3168             # print "Calling Copy for function '$Name'\n";
3169             return 1;
3170             }
3171             }
3172             return 0;
3173             }),
3174              
3175             # "Other pars", the parameters which are usually not pdls.
3176              
3177             PDL::PP::Rule->new(["OtherParNames","OtherParTypes"], ["OtherPars","DimObjs"], \&OtherPars_nft),
3178              
3179             PDL::PP::Rule->new(["ParNames","ParObjs"], ["USParNames","USParObjs"], \&sort_pnobjs),
3180              
3181             PDL::PP::Rule->new("DefSyms", "StructName", \&MkDefSyms),
3182             PDL::PP::Rule->new("NewXSArgs", ["USParNames","USParObjs","OtherParNames","OtherParTypes"],
3183             \&NXArgs),
3184              
3185             PDL::PP::Rule::Returns->new("PMCode", undef),
3186              
3187             PDL::PP::Rule->new("NewXSSymTab", ["DefSyms","NewXSArgs"], \&AddArgsyms),
3188              
3189             PDL::PP::Rule->new("InplaceCode", ["Name","NewXSArgs","USParObjs","_Inplace"],
3190             'Insert code (just after HdrCode) to ensure the routine can be done inplace',
3191             \&InplaceCode),
3192              
3193             PDL::PP::Rule::Returns::EmptyString->new("HdrCode", [],
3194             'Code that will be inserted at the end of the autogenerated xs argument processing code VargArgsXSHdr'),
3195              
3196              
3197             # Create header for variable argument list. Used if no 'other pars' specified.
3198             # D. Hunt 4/11/00
3199             # make sure it is not used when the GlobalNew flag is set ; CS 4/15/00
3200             PDL::PP::Rule->new("VarArgsXSHdr",
3201             ["Name","NewXSArgs","USParObjs","OtherParTypes",
3202             "PMCode","HdrCode","InplaceCode","_GlobalNew","_CallCopy","_Bitwise"],
3203             'XS code to process arguments on stack based on supplied Pars argument to pp_def; GlobalNew has implications how/if this is done',
3204             \&VarArgsXSHdr),
3205              
3206             ## Added new line for returning (or not returning) variables. D. Hunt 4/7/00
3207             # make sure it is not used when the GlobalNew flag is set ; CS 4/15/00
3208             #
3209             PDL::PP::Rule->new("VarArgsXSReturn",
3210             ["NewXSArgs","USParObjs","_GlobalNew"],
3211             "Generate XS trailer for returning output variables",
3212             \&VarArgsXSReturn),
3213              
3214             PDL::PP::Rule->new("NewXSHdr", ["NewXSName","NewXSArgs"], \&XSHdr),
3215             PDL::PP::Rule->new("NewXSCHdrs", ["NewXSName","NewXSArgs","GlobalNew"], \&XSCHdrs),
3216             PDL::PP::Rule->new("NewXSLocals", "NewXSSymTab", \&Sym2Loc),
3217              
3218             PDL::PP::Rule::Returns::Zero->new("IsAffineFlag"),
3219             PDL::PP::Rule::Returns::Zero->new("NoPdlThread"),
3220              
3221             # hmm, need to check on conditional check here (or rather, other bits of code prob need
3222             # to include it too; see Ops.xs, PDL::assgn)
3223             ##
3224             ## sub { return (defined $_[0]) ? "int \$BADFLAGCACHE() = 0;" : ""; } ],
3225             ##
3226             ## why have I got a "_HandleBad" condition here? it isn't used in the routine
3227             ## and isn't required to fire the rule. Or should we actually check the value of
3228             ## HandleBad (ie to optimize for code that explicitly doesn't handle bad code)?
3229             ## TO DO: Check assgn in ops for this? Not obvious, or at least we need other
3230             ## bits of code work with us (eg the checking of $BADFLAGCACHE in some other
3231             ## rule)
3232             ##
3233             ## PDL::PP::Rule->new("CacheBadFlagInitNS", "_HandleBad",
3234             ## sub { return $bvalflag ? "\n int \$BADFLAGCACHE() = 0;\n" : ""; }),
3235             PDL::PP::Rule->new("CacheBadFlagInitNS",
3236             sub { PDL::PP::pp_line_numbers(__LINE__, $bvalflag ? "\n int \$BADFLAGCACHE() = 0;\n" : "") }),
3237             # The next rule, if done in place of the above, causes Ops.xs to fail to compile
3238             # PDL::PP::Rule->new("CacheBadFlagInitNS", "BadFlag",
3239             # sub { return $_[0] ? "\n int \$BADFLAGCACHE() = 0;\n" : ""; }),
3240             PDL::PP::Rule::Substitute::Usual->new("CacheBadFlagInit", "CacheBadFlagInitNS"),
3241              
3242             # need special cases for
3243             # a) bad values
3244             # b) bad values + GlobalNew
3245             # c) bad values + PMCode
3246             # - perhaps I should have separate rules (but b and c produce the
3247             # same output...)
3248             #
3249             PDL::PP::Rule->new("NewXSStructInit0",
3250             ["NewXSSymTab","VTableName","IsAffineFlag","NoPdlThread"],
3251             "Rule to create and initialise the private trans structure",
3252             \&MkPrivStructInit),
3253              
3254             PDL::PP::Rule->new("NewXSMakeNow", ["ParNames","NewXSSymTab"], \&MakeNows),
3255             PDL::PP::Rule->new("IgnoreTypesOf", "FTypes", sub {return {map {($_,1)} keys %{$_[0]}}}),
3256             PDL::PP::Rule::Returns->new("IgnoreTypesOf", {}),
3257              
3258             PDL::PP::Rule->new("NewXSCoerceMustNS", "FTypes", \&make_newcoerce),
3259             PDL::PP::Rule::Substitute::Usual->new("NewXSCoerceMust", "NewXSCoerceMustNS"),
3260              
3261             PDL::PP::Rule::Substitute::Usual->new("DefaultFlowCode", "DefaultFlowCodeNS"),
3262              
3263             PDL::PP::Rule->new("NewXSFindDatatypeNS",
3264             ["ParNames","ParObjs","IgnoreTypesOf","NewXSSymTab","GenericTypes","_P2Child"],
3265             \&find_datatype),
3266             PDL::PP::Rule::Substitute::Usual->new("NewXSFindDatatype", "NewXSFindDatatypeNS"),
3267              
3268             PDL::PP::Rule::Returns::EmptyString->new("NewXSTypeCoerce", "NoConversion"),
3269              
3270             PDL::PP::Rule->new("NewXSTypeCoerceNS",
3271             ["ParNames","ParObjs","IgnoreTypesOf","NewXSSymTab","_P2Child"],
3272             \&coerce_types),
3273             PDL::PP::Rule::Substitute::Usual->new("NewXSTypeCoerce", "NewXSTypeCoerceNS"),
3274              
3275             PDL::PP::Rule::Returns::EmptyString->new("NewXSStructInit1", ["ParNames","NewXSSymTab"]),
3276              
3277             PDL::PP::Rule->new("NewXSSetTrans", ["ParNames","ParObjs","NewXSSymTab"], \&makesettrans),
3278              
3279             PDL::PP::Rule->new("ParsedCode",
3280             ["Code","_BadCode","ParNames","ParObjs","DimObjs","GenericTypes",
3281             "ExtraGenericLoops","HaveThreading","Name"],
3282             sub { return PDL::PP::Code->new(@_); }),
3283             PDL::PP::Rule->new("ParsedBackCode",
3284             ["BackCode","_BadBackCode","ParNames","ParObjs","DimObjs","GenericTypes",
3285             "ExtraGenericLoops","HaveThreading","Name"],
3286             sub { return PDL::PP::Code->new(@_, undef, undef, 'BackCode2'); }),
3287              
3288             # Compiled representations i.e. what the xsub function leaves
3289             # in the trans structure. By default, copies of the parameters
3290             # but in many cases (e.g. slice) a benefit can be obtained
3291             # by parsing the string in that function.
3292              
3293             # If the user wishes to specify his own code and compiled representation,
3294             # The next two definitions allow this.
3295             # Because of substitutions that will be there,
3296             # makecompiledrepr et al are array refs, 0th element = string,
3297             # 1th element = hashref of translated names
3298             # This makes the objects: type + ...
3299             #
3300             PDL::PP::Rule->new(["CompNames","CompObjs"], "Comp", \&OtherPars_nft),
3301             PDL::PP::Rule->new("CompiledRepr", ["CompNames","CompObjs"], \&NT2Decls_p),
3302             PDL::PP::Rule::MakeComp->new("MakeCompiledRepr", ["MakeComp","CompNames","CompObjs"],
3303             "COMP"),
3304              
3305             PDL::PP::Rule->new("CompCopyCode", ["CompNames","CompObjs","CopyName"], \&NT2Copies_p),
3306             PDL::PP::Rule->new("CompFreeCode", ["CompNames","CompObjs"], \&NT2Free_p),
3307              
3308             # This is the default
3309             #
3310             PDL::PP::Rule->new("MakeCompiledRepr",
3311             ["OtherParNames","OtherParTypes","NewXSSymTab"],
3312             \&CopyOtherPars),
3313             PDL::PP::Rule->new("CompiledRepr",
3314             ["OtherParNames","OtherParTypes"],
3315             \&NT2Decls),
3316             PDL::PP::Rule->new("CompCopyCode",
3317             ["OtherParNames","OtherParTypes","CopyName"],
3318             \&NT2Copies_p),
3319             PDL::PP::Rule->new("CompFreeCode", ["OtherParNames","OtherParTypes"], \&NT2Free_p),
3320              
3321             # Threads
3322             #
3323             PDL::PP::Rule->new(["Priv","PrivIsInc"],
3324             ["ParNames","ParObjs","DimObjs","HaveThreading"],
3325             \&make_incsizes),
3326             PDL::PP::Rule->new("PrivCopyCode",
3327             ["ParNames","ParObjs","DimObjs","CopyName","HaveThreading"],
3328             \&make_incsize_copy),
3329             PDL::PP::Rule->new("PrivFreeCode",
3330             ["ParNames","ParObjs","DimObjs","HaveThreading"],
3331             "Frees the thread",
3332             \&make_incsize_free),
3333              
3334             PDL::PP::Rule::Returns->new("RedoDimsCode", [],
3335             'Code that can be inserted to set the size of output piddles dynamically based on input piddles; is parsed',
3336             'PDL_COMMENT("none")'),
3337             PDL::PP::Rule->new("RedoDimsParsedCode",
3338             ["RedoDimsCode","_BadRedoDimsCode","ParNames","ParObjs","DimObjs",
3339             "GenericTypes","ExtraGenericLoops","HaveThreading","Name"],
3340             'makes the parsed representation from the supplied RedoDimsCode',
3341             sub {
3342             return 'PDL_COMMENT("no RedoDimsCode")'
3343             if $_[0] =~ m|^/[*] none [*]/$|;
3344             PDL::PP::Code->new(@_,1); }),
3345             PDL::PP::Rule->new("RedoDims",
3346             ["ParNames","ParObjs","DimObjs","DimmedPars","RedoDimsParsedCode", '_NoPthread'],
3347             'makes the redodims function from the various bits and pieces',
3348             \&make_redodims_thread),
3349              
3350             PDL::PP::Rule::Returns::EmptyString->new("Priv"),
3351              
3352             PDL::PP::Rule->new(["PrivNames","PrivObjs"], "Priv", \&OtherPars_nft),
3353             PDL::PP::Rule->new("PrivateRepr", ["PrivNames","PrivObjs"], \&NT2Decls_p),
3354             PDL::PP::Rule->new("PrivCopyCode", ["PrivNames","PrivObjs","CopyName"], \&NT2Copies_p),
3355              
3356             # avoid clash with freecode above?
3357             #
3358             PDL::PP::Rule->new("NTPrivFreeCode", ["PrivNames","PrivObjs"], \&NT2Free_p),
3359              
3360             PDL::PP::Rule->new("IsReversibleCodeNS", "Reversible", \&ToIsReversible),
3361             PDL::PP::Rule::Substitute::Usual->new("IsReversibleCode", "IsReversibleCodeNS"),
3362              
3363             # Needs cleaning up. NewXSStructInit2DJB has been added to make use
3364             # of the PDL::PP::Rule::Substitute class.
3365             #
3366             PDL::PP::Rule::Substitute->new("NewXSStructInit2DJB", "MakeCompiledRepr"),
3367             PDL::PP::Rule->new("NewXSStructInit2", "NewXSStructInit2DJB",
3368             sub { PDL::PP::pp_line_numbers(__LINE__, "{".$_[0]."}") }),
3369              
3370             PDL::PP::Rule->new("CopyCodeNS",
3371             ["PrivCopyCode","CompCopyCode","StructName","NoPdlThread"],
3372             sub {
3373             PDL::PP::pp_line_numbers(__LINE__,
3374             "$_[2] *__copy = malloc(sizeof($_[2])); memset(__copy, 0, sizeof($_[2]));\n" .
3375             ($_[3] ? "" : "PDL_THR_CLRMAGIC(&__copy->__pdlthread);") .
3376             " PDL_TR_CLRMAGIC(__copy);
3377             __copy->has_badvalue = \$PRIV(has_badvalue);
3378             __copy->badvalue = \$PRIV(badvalue);
3379             __copy->flags = \$PRIV(flags);
3380             __copy->vtable = \$PRIV(vtable);
3381             __copy->__datatype = \$PRIV(__datatype);
3382             __copy->freeproc = NULL;
3383             __copy->__ddone = \$PRIV(__ddone);
3384             {int i;
3385             for(i=0; i<__copy->vtable->npdls; i++)
3386             __copy->pdls[i] = \$PRIV(pdls[i]);
3387             }
3388             $_[1]
3389             if(__copy->__ddone) {
3390             $_[0]
3391             }
3392             return (pdl_trans*)__copy;") }),
3393              
3394             PDL::PP::Rule->new("FreeCodeNS",
3395             ["PrivFreeCode","CompFreeCode","NTPrivFreeCode"],
3396             sub {
3397             PDL::PP::pp_line_numbers(__LINE__, "
3398             PDL_TR_CLRMAGIC(__privtrans);
3399             $_[1]
3400             if(__privtrans->__ddone) {
3401             $_[0]
3402             $_[2]
3403             }
3404             ") }),
3405              
3406             PDL::PP::Rule::Substitute::Usual->new("CopyCode", "CopyCodeNS"),
3407             PDL::PP::Rule::Substitute::Usual->new("FreeCode", "FreeCodeNS"),
3408             PDL::PP::Rule::Substitute::Usual->new("FooCodeSub", "FooCode"),
3409              
3410             PDL::PP::Rule::Returns::EmptyString->new("NewXSCoerceMust"),
3411              
3412             PDL::PP::Rule::MakeComp->new("NewXSCoerceMustSub1", "NewXSCoerceMust", "FOO"),
3413             PDL::PP::Rule::Substitute->new("NewXSCoerceMustSub1d", "NewXSCoerceMustSub1"),
3414              
3415             PDL::PP::Rule->new("NewXSClearThread", "HaveThreading",
3416             sub {$_[0] ? PDL::PP::pp_line_numbers(__LINE__, "__privtrans->__pdlthread.inds = 0;") : ""}),
3417              
3418             PDL::PP::Rule->new("NewXSFindBadStatusNS",
3419             ["BadFlag","_FindBadStatusCode","NewXSArgs","USParObjs","OtherParTypes","NewXSSymTab","Name"],
3420             "Rule to find the bad value status of the input piddles",
3421             \&findbadstatus),
3422              
3423             # this can be removed once the default bad values are stored in a C structure
3424             # (rather than as a perl array in PDL::Types)
3425             # which it now is, hence the comments (DJB 07/10/00)
3426             # - left around in case we move to per-piddle bad values
3427             # - NOTE: now we have the experimental per-piddle bad values I need to remember
3428             # what I was doing here
3429             # [[NewXSCopyBadValues], [BadFlag,NewXSSymTab],
3430             # "copybadvalues",
3431             # "Rule to copy the default bad values into the trnas structure"],
3432              
3433             PDL::PP::Rule->new("NewXSCopyBadStatusNS",
3434             ["BadFlag","_CopyBadStatusCode","NewXSArgs","USParObjs","NewXSSymTab"],
3435             "Rule to copy the bad value status to the output piddles",
3436             \©badstatus),
3437              
3438             # expand macros in ...BadStatusCode
3439             #
3440             PDL::PP::Rule::Substitute::Usual->new("NewXSFindBadStatus", "NewXSFindBadStatusNS"),
3441             PDL::PP::Rule::Substitute::Usual->new("NewXSCopyBadStatus", "NewXSCopyBadStatusNS"),
3442              
3443             # Generates XS code with variable argument list. If this rule succeeds, the next rule
3444             # will not be executed. D. Hunt 4/11/00
3445             #
3446             PDL::PP::Rule->new(["NewXSCode","BootSetNewXS","NewXSInPrelude"],
3447             ["_GlobalNew","_NewXSCHdrs","VarArgsXSHdr","NewXSLocals",
3448             "CacheBadFlagInit",
3449             "NewXSStructInit0",
3450             "NewXSFindBadStatus",
3451             # NewXSCopyBadValues,
3452             # NewXSMakeNow, # this is unnecessary since families never got implemented
3453             "NewXSFindDatatype","NewXSTypeCoerce",
3454             "NewXSStructInit1",
3455             "NewXSStructInit2",
3456             "NewXSCoerceMustSub1d","_IsReversibleCode","DefaultFlowCode",
3457             "NewXSClearThread",
3458             "NewXSSetTrans",
3459             "NewXSCopyBadStatus",
3460             "VarArgsXSReturn"
3461             ],
3462             "Rule to print out XS code when variable argument list XS processing is enabled",
3463             \&mkVarArgsxscat),
3464              
3465             # This rule will fail if the preceding rule succeeds
3466             # D. Hunt 4/11/00
3467             #
3468             PDL::PP::Rule->new(["NewXSCode","BootSetNewXS","NewXSInPrelude"],
3469             ["_GlobalNew","_NewXSCHdrs","NewXSHdr","NewXSLocals",
3470             "CacheBadFlagInit",
3471             "NewXSStructInit0",
3472             "NewXSFindBadStatus",
3473             # NewXSCopyBadValues,
3474             # NewXSMakeNow, # this is unnecessary since families never got implemented
3475             "NewXSFindDatatype","NewXSTypeCoerce",
3476             "NewXSStructInit1",
3477             "NewXSStructInit2",
3478             "NewXSCoerceMustSub1d","_IsReversibleCode","DefaultFlowCode",
3479             "NewXSClearThread",
3480             "NewXSSetTrans",
3481             "NewXSCopyBadStatus"
3482             ],
3483             "Rule to print out XS code when variable argument list XS processing is disabled",
3484             \&mkxscat),
3485              
3486             PDL::PP::Rule->new("StructDecl",
3487             ["ParNames","ParObjs","CompiledRepr","PrivateRepr","StructName"],
3488             \&mkstruct),
3489              
3490             # The RedoDimsSub rule is a bit weird since it takes in the RedoDims target
3491             # twice (directly and via RedoDims-PostComp). Can this be cleaned up?
3492             # [I don't know who put this in, or when -- but I don't understand it. CED 13-April-2015]
3493             PDL::PP::Rule->new("RedoDims-PreComp", "RedoDims",
3494             sub { PDL::PP::pp_line_numbers(__LINE__, $_[0] . ' $PRIV(__ddone) = 1;') }),
3495             PDL::PP::Rule::MakeComp->new("RedoDims-PostComp",
3496             ["RedoDims-PreComp", "PrivNames", "PrivObjs"], "PRIV"),
3497              
3498             # RedoDimsSub is supposed to allow you to use $SIZE as an lvalue, to resize things. It hasn't
3499             # worked since I can remember (at least since I started messing around with range). The reason
3500             # appears to be that the SIZE macro was using the redodims argument instead of its own zeroth
3501             # argument. Renaming gone wrong? Anyway I've fixed it to use $_[0] instead of $redodims in the
3502             # SIZE closure. -- CED 13-April-2015
3503             PDL::PP::Rule->new("RedoDimsSub",
3504             ["RedoDims", "RedoDims-PostComp", "_DimObjs"],
3505             sub {
3506             my $redodims = $_[0];
3507             my $result = $_[1];
3508             my $dimobjs = $_[2];
3509              
3510             $result->[1]{"SIZE"} = sub {
3511             eval 'use PDL::IO::Dumper';
3512             croak "FOO can't get SIZE of undefined dimension (RedoDims=$redodims).\nredodims is $redodims\ndimobjs is ".sdump($dimobjs)."\n"
3513             unless defined $dimobjs->{$_[0]}; # This is the closure's $_[0], not the rule definition's $_[0]
3514             return $dimobjs->{$_[0]}->get_size();
3515             };
3516             return $result;
3517             }),
3518             PDL::PP::Rule::Substitute->new("RedoDimsSubd", "RedoDimsSub"),
3519             PDL::PP::Rule->new("RedoDimsFunc",
3520             ["RedoDimsSubd","FHdrInfo","RedoDimsFuncName","_P2Child"],
3521             sub {wrap_vfn(@_,"redodims")}),
3522              
3523             PDL::PP::Rule::MakeComp->new("ReadDataSub", "ParsedCode", "FOO"),
3524             PDL::PP::Rule::Substitute->new("ReadDataSubd", "ReadDataSub"),
3525             PDL::PP::Rule->new("ReadDataFunc",
3526             ["ReadDataSubd","FHdrInfo","ReadDataFuncName","_P2Child"],
3527             sub {wrap_vfn(@_,"readdata")}),
3528              
3529             PDL::PP::Rule::MakeComp->new("WriteBackDataSub", "ParsedBackCode", "FOO"),
3530             PDL::PP::Rule::Substitute->new("WriteBackDataSubd", "WriteBackDataSub"),
3531              
3532             PDL::PP::Rule::InsertName->new("WriteBackDataFuncName", "BackCode", 'pdl_${name}_writebackdata'),
3533             PDL::PP::Rule::Returns::NULL->new("WriteBackDataFuncName", "Code"),
3534              
3535             PDL::PP::Rule->new("WriteBackDataFunc",
3536             ["WriteBackDataSubd","FHdrInfo","WriteBackDataFuncName","_P2Child"],
3537             sub {wrap_vfn(@_,"writebackdata")}),,
3538              
3539             PDL::PP::Rule->new("CopyFunc",
3540             ["CopyCode","FHdrInfo","CopyFuncName","_P2Child"],
3541             sub {wrap_vfn(@_,"copy")}),
3542             PDL::PP::Rule->new("FreeFunc",
3543             ["FreeCode","FHdrInfo","FreeFuncName","_P2Child"],
3544             sub {wrap_vfn(@_,"free")}),
3545              
3546             PDL::PP::Rule::Returns->new("FoofName", "FooCodeSub", "foomethod"),
3547             PDL::PP::Rule->new("FooFunc", ["FooCodeSub","FHdrInfo","FoofName","_P2Child"],
3548             sub {wrap_vfn(@_,"foo")}),
3549              
3550             PDL::PP::Rule::Returns::NULL->new("FoofName"),
3551              
3552             PDL::PP::Rule->new("VTableDef",
3553             ["VTableName","StructName","RedoDimsFuncName","ReadDataFuncName",
3554             "WriteBackDataFuncName","CopyFuncName","FreeFuncName",
3555             "ParNames","ParObjs","Affine_Ok","FoofName"],
3556             \&def_vtable),
3557              
3558             # Maybe accomplish this with an InsertName rule?
3559             PDL::PP::Rule->new('PMFunc', 'Name',
3560             'Sets PMFunc to default symbol table manipulations',
3561             sub {
3562             my ($name) = @_;
3563             $::PDL_IFBEGINWRAP[0].'*'.$name.' = \&'.$::PDLOBJ.
3564             '::'.$name.";\n".$::PDL_IFBEGINWRAP[1]
3565             }
3566             ),
3567              
3568             ];
3569              
3570             sub printtrans {
3571 0     0 0 0 my($bar) = @_;
3572 0         0 for (qw/StructDecl RedoDimsFunc ReadDataFunc WriteBackFunc
3573             VTableDef NewXSCode/) {
3574             print "\n\n================================================
3575             $_
3576 0 0       0 =========================================\n",$bar->{$_},"\n" if $::PP_VERBOSE;
3577             }
3578             }
3579              
3580             sub translate {
3581 2     2 0 5 my ($pars,$tbl) = @_;
3582              
3583 2         7 foreach my $rule (@$tbl) {
3584 209         484 $rule->apply($pars);
3585             }
3586              
3587             # print Dumper($pars);
3588 1 50       3 print "GOING OUT!\n" if $::PP_VERBOSE;
3589 1         3 return $pars;
3590             } # sub: translate()
3591              
3592             ## End
3593             #