File Coverage

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