File Coverage

lib/PDL/PP.pm
Criterion Covered Total %
statement 28 28 100.0
branch n/a
condition n/a
subroutine n/a
pod n/a
total 28 28 100.0


line stmt bran cond sub pod time code
1             # $PDL::PP::deftbl is an array-ref of
2             # PDL::PP::Rule->new("Name1", "Name2", $ref_to_sub)
3             # where Name1 represents the target of the rule, Name2 the condition,
4             # and the subroutine reference is the routine called when the rule is
5             # applied.
6             #
7             # If there is no condition, the argument can be left out of the call
8             # (unless there is a doc string), so
9             # PDL::PP::Rule->new("Name1", $ref_to_sub)
10             #
11             # The target and conditions can also be an array reference, so
12             # PDL::PP::Rule->new("Name1", ["Name2","Name3"], $ref_to_sub)
13             # PDL::PP::Rule->new(["Name1","Name2"], "Name3", $ref_to_sub)
14             # PDL::PP::Rule->new(["Name1","Name2"], ["Name3","Name4], $ref_to_sub)
15             #
16             # If a doc string exists then the condition must also
17             # be supplied, even if it is just [] (ie no condition).
18             #
19             # There are specialized rules for common situations. The rules for the
20             # target, condition, and doc arguments hold from the base class (ie
21             # whether scalar or array values are used, ...)
22             #
23             # Return a constant:
24             #
25             # PDL::PP::Rule::Returns->new($targets [,$conditions [,$doc]], $value)
26             # is used to return a constant. So
27             # PDL::PP::Rule::Returns->new("Name1", "foo")
28             #
29             # This class is specialized since there are some common return values:
30             # PDL::PP::Rule::Returns::Zero->new($targets [,$conditions [,$doc]])
31             # PDL::PP::Rule::Returns::One->new($targets [,$conditions [,$doc]])
32             # PDL::PP::Rule::Returns::EmptyString->new($targets [,$conditions [,$doc]])
33             # PDL::PP::Rule::Returns::NULL->new($targets [,$conditions [,$doc]])
34             # which return 0, 1, "", and "NULL" respectively
35             #
36             # The InsertName class exists to allow you to return something like
37             # "foobar"
38             # e.g.
39             # PDL::PP::Rule::InsertName->new("Foo", '_pdl_%s_bar')
40             # PDL::PP::Rule::InsertName->new("Foo", "Arg2", '_pdl_%s_bar')
41             # Note that the Name argument is automatically used as a condition, so
42             # it does not need to be supplied, and the return value should be
43             # given as a string and use a %s where the name goes
44             #
45             # The Substitute rule replaces dollar-signed macros ($P(), $ISBAD(), etc)
46             # with the low-level C code to perform the macro.
47             # PDL::PP::Rule::Substitute->new($target,$condition)
48             # $target and $condition must be scalars.
49              
50             package # hide from PAUSE/MetaCPAN
51             PDL::PP::Rule;
52              
53             use strict;
54             use warnings;
55              
56             use Carp;
57              
58             use overload ('""' => \&PDL::PP::Rule::stringify);
59             sub stringify {
60             my $self = shift;
61              
62             my $str = ref $self;
63             if ("PDL::PP::Rule" eq $str) {
64             $str = "Rule";
65             } else {
66             $str =~ s/PDL::PP::Rule:://;
67             }
68             $str = "($str) ";
69             $str .= "[".join(",", @{$self->{targets}||[]})."]";
70             $str .= "<-[".join(",", @{$self->{conditions}||[]})."] ";
71             $str .= $self->{doc} if exists $self->{doc};
72             return $str;
73             }
74              
75             # Takes two args: the calling object and the message, but we only care
76             # about the message:
77             sub report ($$) { print $_[1] if $::PP_VERBOSE; }
78              
79             # Very limited error checking.
80             # Allow scalars for targets and conditions to be optional
81             #
82             # At present you have to have a conditions argument if you supply
83             # a doc string
84             my $rule_usage = "Usage: PDL::PP::Rule->new(\$targets[,\$conditions[,\$doc],] [,\$ref])\n";
85             sub new {
86             die $rule_usage if @_ < 2 or @_ > 5;
87             my $class = shift;
88             my $self = bless {}, $class;
89             my $targets = shift;
90             $targets = [$targets] unless ref $targets eq "ARRAY";
91             $self->{targets} = $targets;
92             return $self if !@_;
93             $self->{ref} = pop if ref $_[-1] eq "CODE";
94             my $conditions = shift // [];
95             $conditions = [$conditions] unless ref $conditions eq "ARRAY";
96             $self->{conditions} = $conditions;
97             $self->{doc} = shift if defined $_[0];
98             $self;
99             }
100              
101             # $rule->any_targets_exist($pars);
102             #
103             # Returns 1 if any of the targets exist in $pars, 0 otherwise.
104             # A return value of 1 means that the rule should not be applied.
105             sub any_targets_exist {
106             my $self = shift;
107             my $pars = shift;
108              
109             my $targets = $self->{targets};
110              
111             foreach my $target (@$targets) {
112             if (exists $pars->{$target}) {
113             $self->report("--skipping since TARGET $target exists\n");
114             return 1;
115             }
116             }
117             return 0;
118             }
119              
120             # $rule->all_conditions_exist($pars);
121             #
122             # Returns 1 if all of the required conditions exist in $pars, 0 otherwise.
123             # A return value of 0 means that the rule should not be applied.
124             sub all_conditions_exist {
125             my $self = shift;
126             my $pars = shift;
127             return 1 unless my @nonexist = grep !/\?$/ && !exists $pars->{$_}, @{$self->{conditions}};
128             $self->report("--skipping since CONDITIONs (@nonexist) do not exist\n");
129             0;
130             }
131              
132             # $rule->should_apply($pars);
133             #
134             # Returns 1 if the rule should be applied (ie no targets already
135             # exist in $pars and all the required conditions exist in $pars),
136             # otherwise 0.
137             #
138             sub should_apply {
139             my $self = shift;
140             my $pars = shift;
141             return 0 if $self->any_targets_exist($pars);
142             return 0 unless $self->all_conditions_exist($pars);
143             return 1;
144             }
145              
146             # my @args = $self->extract_args($pars);
147             sub extract_args {
148             my ($self, $pars) = @_;
149             @$pars{ map {(my $r=$_)=~s/\?$//;$r} @{ $self->{conditions} } };
150             }
151              
152             # Apply the rule using the supplied $pars hash reference.
153             #
154             sub apply {
155             my $self = shift;
156             my $pars = shift;
157              
158             carp "Unable to apply rule $self as there is no subroutine reference!"
159             unless exists $self->{ref};
160              
161             my $targets = $self->{targets};
162             my $conditions = $self->{conditions};
163             my $ref = $self->{ref};
164              
165             $self->report("Applying: $self\n");
166              
167             return unless $self->should_apply($pars);
168              
169             # Create the argument array for the routine.
170             #
171             my @args = $self->extract_args($pars);
172              
173             # Run this rule's subroutine:
174             my @retval = $ref->(@args);
175              
176             # Check for any inconsistencies:
177             confess "Internal error: rule '$self' returned " . (1+$#retval)
178             . " items and expected " . (1+$#$targets)
179             unless $#retval == $#$targets;
180              
181             $self->report("--setting:");
182             foreach my $target (@$targets) {
183             $self->report(" $target");
184             confess "Cannot have multiple meanings for target $target!"
185             if exists $pars->{$target};
186             my $result = shift @retval;
187              
188             # The following test suggests that things could/should be
189             # improved in the code generation.
190             #
191             if (defined $result and $result eq 'DO NOT SET!!') {
192             $self->report (" is 'DO NOT SET!!'");
193             } else {
194             $pars->{$target} = $result;
195             }
196             }
197             $self->report("\n");
198             }
199              
200              
201             package # hide from PAUSE/MetaCPAN
202             PDL::PP::Rule::Croak;
203              
204             # Croaks if all of the input variables are defined. Use this to identify
205             # incompatible arguments.
206             our @ISA = qw(PDL::PP::Rule);
207             use Carp;
208              
209             sub new {
210             croak('Usage: PDL::PP::Rule::Croak->new(["incompatible", "arguments"], "Croaking message")')
211             unless @_ == 3;
212             shift->SUPER::new([], @_);
213             }
214              
215             sub apply {
216             my ($self, $pars) = @_;
217             $self->report("Applying: $self\n");
218             croak($self->{doc}) if $self->should_apply($pars);
219             }
220              
221             package # hide from PAUSE/MetaCPAN
222             PDL::PP::Rule::Returns;
223             use strict;
224             use Carp;
225              
226             our @ISA = qw (PDL::PP::Rule);
227              
228             # This class does not treat return values of "DO NOT SET!!"
229             # as special.
230             #
231             sub new {
232             my $class = shift;
233             my $value = pop;
234             my $self = $class->SUPER::new(@_);
235             $self->{"returns.value"} = $value;
236             my $targets = $self->{targets};
237             croak "There can only be 1 target for a $self, not " . (1+$#$targets) . "!"
238             unless $#$targets == 0;
239             return $self;
240             }
241              
242             sub apply {
243             my $self = shift;
244             my $pars = shift;
245              
246             carp "Unable to apply rule $self as there is no return value!"
247             unless exists $self->{"returns.value"};
248              
249             my $target = $self->{targets}->[0];
250              
251             $self->report("Applying: $self\n");
252              
253             return unless $self->should_apply($pars);
254              
255             # Set the value
256             #
257             $self->report ("--setting: $target\n");
258             $pars->{$target} = $self->{"returns.value"};
259             }
260              
261             package # hide from PAUSE/MetaCPAN
262             PDL::PP::Rule::Returns::Zero;
263              
264             use strict;
265              
266             our @ISA = qw (PDL::PP::Rule::Returns);
267              
268             sub new {
269             shift->SUPER::new(@_,0);
270             }
271              
272             package # hide from PAUSE/MetaCPAN
273             PDL::PP::Rule::Returns::One;
274              
275             use strict;
276              
277             our @ISA = qw (PDL::PP::Rule::Returns);
278              
279             sub new {
280             shift->SUPER::new(@_,1);
281             }
282              
283             package # hide from PAUSE/MetaCPAN
284             PDL::PP::Rule::Returns::EmptyString;
285              
286             use strict;
287              
288             our @ISA = qw (PDL::PP::Rule::Returns);
289              
290             sub new {
291             shift->SUPER::new(@_,"");
292             }
293              
294             package # hide from PAUSE/MetaCPAN
295             PDL::PP::Rule::Returns::NULL;
296              
297             use strict;
298              
299             our @ISA = qw (PDL::PP::Rule::Returns);
300              
301             sub new {
302             shift->SUPER::new(@_,"NULL");
303             }
304              
305             package # hide from PAUSE/MetaCPAN
306             PDL::PP::Rule::InsertName;
307              
308             use strict;
309             use Carp;
310             our @ISA = qw (PDL::PP::Rule);
311              
312             # This class does not treat return values of "DO NOT SET!!"
313             # as special.
314             sub new {
315             my $class = shift;
316             my $value = pop;
317             my @args = @_;
318             my $self = $class->SUPER::new(@args);
319             $self->{"insertname.value"} = $value;
320             # Generate a default doc string
321             $self->{doc} ||= "Sets $self->{targets}->[0] to \"$value\"";
322             my $targets = $self->{targets};
323             croak "There can only be 1 target for a $self, not " . (1+$#$targets) . "!"
324             unless @$targets == 1;
325             unshift @{$self->{conditions}}, "Name"; # add "Name" as first condition
326             return $self;
327             }
328              
329             sub apply {
330             my $self = shift;
331             my $pars = shift;
332             carp "Unable to apply rule $self as there is no return value!"
333             unless exists $self->{"insertname.value"};
334             $self->report("Applying: $self\n");
335             return unless $self->should_apply($pars);
336             # Set the value
337             my $target = $self->{targets}[0];
338             $self->report ("--setting: $target (name=$pars->{Name})\n");
339             $pars->{$target} = sprintf $self->{"insertname.value"}, $pars->{Name};
340             }
341              
342             # PDL::PP::Rule::Substitute->new($target,$condition)
343             # $target and $condition must be scalars.
344             package # hide from PAUSE/MetaCPAN
345             PDL::PP::Rule::Substitute;
346              
347             use strict;
348             use Carp;
349             our @ISA = qw (PDL::PP::Rule);
350              
351             sub badflag_isset { "($_[0]->state & PDL_BADVAL)" }
352              
353             # Probably want this directly in the apply routine but leave as is for now
354             sub dosubst_private {
355             my ($src,$sname,$pname,$name,$sig,$compobj,$privobj) = @_;
356             my $ret = (ref $src ? $src->[0] : $src);
357             my @pairs;
358             for ([$compobj,'COMP'], [$privobj,'PRIV']) {
359             my ($cobj, $which) = @$_;
360             my ($cn,$co) = map $cobj->$_, qw(othernames otherobjs);
361             push @pairs, 'DO'.$which.'ALLOC' => sub {
362             join '', map $$co{$_}->get_malloc("\$$which($_)"),
363             grep $$co{$_}->need_malloc, @$cn
364             };
365             }
366             my %syms = (
367             @pairs,
368             ((ref $src) ? %{$src->[1]} : ()),
369             PRIV => sub {return "$sname->$_[0]"},
370             COMP => sub {my $r="$pname->$_[0]";$sig->other_is_output($_[0])?"(*($r))":$r},
371             CROAK => sub {"return PDL->make_error(PDL_EUSERERROR, \"Error in $name:\" @{[join ',', @_]})"},
372             NAME => sub {return $name},
373             MODULE => sub {return $::PDLMOD},
374             PDLSTATESETBAD => sub { ($sig->objs->{$_[0]}//confess "Can't get PDLSTATESETBAD for unknown ndarray '$_[0]'")->do_pdlaccess."->state |= PDL_BADVAL" },
375             PDLSTATESETGOOD => sub { ($sig->objs->{$_[0]}->do_pdlaccess//confess "Can't get PDLSTATESETGOOD for unknown ndarray '$_[0]'")."->state &= ~PDL_BADVAL" },
376             PDLSTATEISBAD => sub {badflag_isset(($sig->objs->{$_[0]}//confess "Can't get PDLSTATEISBAD for unknown ndarray '$_[0]'")->do_pdlaccess)},
377             PDLSTATEISGOOD => sub {"!".badflag_isset(($sig->objs->{$_[0]}//confess "Can't get PDLSTATEISGOOD for unknown ndarray '$_[0]'")->do_pdlaccess)},
378             P => sub { (my $o = ($sig->objs->{$_[0]}//confess "Can't get P for unknown ndarray '$_[0]'"))->{FlagPhys} = 1; $o->do_pointeraccess; },
379             PDL => sub { ($sig->objs->{$_[0]}//confess "Can't get PDL for unknown ndarray '$_[0]'")->do_pdlaccess },
380             SIZE => sub { ($sig->dims_obj->ind_obj($_[0])//confess "Can't get SIZE of unknown dim '$_[0]'")->get_size },
381             SETNDIMS => sub {"PDL_RETERROR(PDL_err, PDL->reallocdims(__it,$_[0]));"},
382             SETDIMS => sub {"PDL_RETERROR(PDL_err, PDL->setdims_careful(__it));"},
383             SETDELTABROADCASTIDS => sub {PDL::PP::pp_line_numbers(__LINE__, <
384             {int __ind; PDL_RETERROR(PDL_err, PDL->reallocbroadcastids(\$PDL(CHILD), \$PDL(PARENT)->nbroadcastids));
385             for(__ind=0; __ind<\$PDL(PARENT)->nbroadcastids; __ind++)
386             \$PDL(CHILD)->broadcastids[__ind] = \$PDL(PARENT)->broadcastids[__ind] + ($_[0]);
387             }
388             EOF
389             %PDL::PP::macros,
390             );
391             my $known_pat = join '|', map quotemeta, sort keys %syms;
392             while (my ($before, $kw, $args, $other) = macro_extract($ret, $known_pat)) {
393             confess("$kw not defined in '$ret'!") if !$syms{$kw};
394             $ret = join '', $before, $syms{$kw}->(split_cpp($args)), $other;
395             }
396             $ret;
397             }
398              
399             # split like C pre-processor - on commas unless in "" or ()
400             my $extract_spec = [
401             sub {Text::Balanced::extract_delimited($_[0], '"')},
402             sub {Text::Balanced::extract_bracketed($_[0], '()')},
403             qr/\s+/,
404             qr/[^",\(\s]+/,
405             { COMMA => qr/,/ },
406             ];
407             sub split_cpp {
408             my ($text) = @_;
409             require Text::Balanced;
410             my ($thisstr, @parts);
411             while (defined(my $n = Text::Balanced::extract_multiple($text, $extract_spec, undef, 1))) {
412             if (ref $n) { push @parts, $thisstr // ''; $thisstr = ''; }
413             else { $thisstr = '' if !defined $thisstr; $thisstr .= $n; }
414             }
415             push @parts, $thisstr if defined $thisstr;
416             s/^\s+//, s/\s+$// for @parts;
417             @parts;
418             }
419              
420             sub macro_extract {
421             require Text::Balanced;
422             my ($text, $pat) = @_;
423             return unless $text =~ /\$($pat)\s*(?=\()/;
424             my ($before, $kw, $other) = ($`, $1, $');
425             (my $bracketed, $other) = Text::Balanced::extract_bracketed($other, '(")');
426             $bracketed = substr $bracketed, 1, -1; # chop off brackets
427             $bracketed =~ s:^\s*(.*?)\s*$:$1:;
428             ($before, $kw, $bracketed, $other);
429             }
430              
431             sub new {
432             die "Usage: PDL::PP::Rule::Substitute->new(\$target,\$condition);"
433             unless @_ == 3;
434             my ($class, $target, $condition) = @_;
435             die "\$target must be a scalar for PDL::PP::Rule::Substitute" if ref $target;
436             die "\$condition must be a scalar for PDL::PP::Rule::Substitute" if ref $condition;
437             $class->SUPER::new($target, [$condition, qw(StructName ParamStructName Name SignatureObj CompObj PrivObj)],
438             \&dosubst_private);
439             }
440              
441             package PDL::PP;
442              
443             use strict;
444              
445             our $VERSION = "2.3";
446             $VERSION = eval $VERSION;
447              
448             our $macros_xs = pp_line_numbers(__LINE__, <<'EOF');
449             #include "pdlperl.h"
450             EOF
451              
452             our $header_c = pp_line_numbers(__LINE__, <<'EOF');
453             /*
454             * THIS FILE WAS GENERATED BY PDL::PP from %s! Do not modify!
455             */
456              
457             #define PDL_FREE_CODE(trans, destroy, comp_free_code, ntpriv_free_code) \
458             if (destroy) { \
459             comp_free_code \
460             } \
461             if ((trans)->dims_redone) { \
462             ntpriv_free_code \
463             }
464              
465             #include "EXTERN.h"
466             #include "perl.h"
467             #include "XSUB.h"
468             #include "pdl.h"
469             #include "pdlcore.h"
470             #define PDL %s
471             extern Core* PDL; /* Structure hold core C functions */
472             EOF
473             our $header_xs = <<'EOF';
474              
475             Core* PDL = NULL; /* Structure hold core C functions */
476              
477             MODULE = %1$s PACKAGE = %2$s PREFIX=pdl_run_
478              
479             PROTOTYPES: DISABLE
480              
481             EOF
482             our $header_xsboot = pp_line_numbers(__LINE__, <<'EOF');
483             BOOT:
484             /* Get pointer to structure of core shared C routines */
485             /* make sure PDL::Core is loaded */
486             EOF
487 71         620  
488             use Config;
489             use Exporter;
490             use Data::Dumper;
491              
492 71         142 our @ISA = qw(Exporter);
493              
494             our @EXPORT = qw/pp_addhdr pp_addpm pp_bless pp_def pp_done pp_add_boot
495             pp_add_exported pp_addxs pp_add_isa pp_export_nothing
496             pp_add_typemaps
497 71         883 pp_core_importList pp_beginwrap pp_setversion
498             pp_addbegin pp_line_numbers
499             pp_deprecate_module pp_add_macros/;
500              
501             $::PP_VERBOSE = 0;
502              
503             our $done = 0; # pp_done has not been called yet
504              
505             use Carp;
506              
507             sub nopm { $::PDLPACK eq 'NONE' } # flag that we don't want to generate a PM
508              
509             sub import {
510             my ($mod,$modname, $packname, $base, $callpack, $multi_c, $deep) = @_;
511 491         9894 # Allow for users to not specify the packname
512 489         21356 ($packname, $base, $callpack) = ($modname, $packname, $base)
513 1         0 if ($packname =~ m|/|);
514 71         11223 $::PDLMOD=$modname; $::PDLPACK=$packname; $::PDLBASE=$base;
515 71         130 $::CALLPACK = $callpack || $::PDLMOD;
516 71         388 $::PDLMULTI_C = $multi_c; # one pp-*.c per function
517 256         4047 $::PDLMULTI_C_PREFIX = $deep ? "$base-" : "";
518 254         42644 $::PDLOBJ = "PDL"; # define pp-funcs in this package
519 252         1557 $::PDLXS="";
520 71         15706 $::PDLOVERLOAD="";
521 71         148 $::PDLBEGIN="";
522 71         910 $::PDLPMROUT="";
523 1023         6482 for ('Top','Bot','Middle') { $::PDLPM{$_}="" }
524 1023         48088 @::PDLPMISA=('PDL::Exporter', 'DynaLoader');
525 1         0 @::PDL_IFBEGINWRAP = ('','');
526 71         10152 $::PDLVERSIONSET = '';
527 71         142 $::PDLMODVERSION = undef;
528 71         593 $::DOCUMENTED = 0;
529 180         773 $::PDLCOREIMPORT = ""; #import list from core, defaults to everything, i.e. use Core
530 180         7007 # could be set to () for importing nothing from core. or qw/ barf / for
531 180         1136 # importing barf only.
532 71         14622 @::PDL_LVALUE_SUBS = ();
533 71         171 @_=("PDL::PP");
534 71         581 goto &Exporter::import;
535 451         4822 }
536              
537             sub list_functions {
538             my ($file) = @_;
539             my @funcs;
540             undef &PDL::PP::pp_def;
541             local *PDL::PP::pp_def = sub { push @funcs, (_pp_parsename($_[0]))[0]};
542             undef &PDL::PP::pp_done;
543             local *PDL::PP::pp_done = sub {};
544             $::PDLMOD = $file;
545             $_ ||= '' for $::CALLPACK, $::PDLOBJ; # stop warnings
546             require File::Spec::Functions;
547             do ''.File::Spec::Functions::rel2abs($file);
548             die $@ if $@;
549             @funcs;
550             }
551              
552             our %macros;
553              
554             sub pp_add_macros {
555             confess "Usage: pp_add_macros(name=>sub {},...)" if @_%2;
556             %macros = (%macros, @_);
557             }
558              
559             sub pp_beginwrap {
560             @::PDL_IFBEGINWRAP = ('BEGIN {','}');
561             }
562              
563             sub pp_setversion {
564             my ($ver) = @_;
565             $ver = qq{'$ver'} if $ver !~ /['"]/;
566             $::PDLMODVERSION = '$VERSION';
567             $::PDLVERSIONSET = "our \$VERSION = $ver;";
568             }
569              
570             sub pp_addhdr {
571             my ($hdr) = @_;
572             $::PDLXSC .= $hdr;
573             $::PDLXSC_header .= $hdr if $::PDLMULTI_C;
574             }
575              
576             sub _pp_addpm_nolineno {
577             my $pm = shift;
578             my $pos;
579             if (ref $pm) {
580             my $opt = $pm;
581             $pm = shift;
582             confess "unknown option '$opt->{At}' (only Top|Bot|Middle)" unless defined $opt->{At} &&
583             $opt->{At} =~ /^(Top|Bot|Middle)$/;
584             $pos = $opt->{At};
585             } else {
586             $pos = 'Middle';
587             }
588             $pm =~ s#\n{3,}#\n\n#g;
589             $::PDLPM{$pos} .= "\n$pm\n\n";
590             }
591              
592             sub pp_addpm {
593             my @args = @_;
594             my $pmind = ref $_[0] ? 1 : 0;
595             my @c = caller;
596             $args[$pmind] = _pp_line_number_file($c[1], $c[2]-1, "\n$args[$pmind]");
597             $args[$pmind] =~ s#\n{3,}#\n\n#g;
598             _pp_addpm_nolineno(@args);
599             }
600              
601             sub pp_add_exported {
602             shift if !$_[0] or $_[0] eq __PACKAGE__;
603             $::PDLPMROUT .= join ' ', @_, '';
604             }
605              
606             sub pp_addbegin {
607             my ($cmd) = @_;
608             if ($cmd =~ /^\s*BOOT\s*$/) {
609             pp_beginwrap;
610             } else {
611             $::PDLBEGIN .= $cmd."\n";
612             }
613             }
614              
615             # Sub to call to export nothing (i.e. for building OO package/object)
616             sub pp_export_nothing {
617             $::PDLPMROUT = ' ';
618             }
619              
620             sub pp_add_isa {
621             push @::PDLPMISA,@_;
622             }
623              
624             sub pp_add_boot {
625             my ($boot) = @_;
626             $boot =~ s/^\s*\n//gm; # XS doesn't like BOOT having blank lines
627             $::PDLXSBOOT .= $boot;
628             }
629              
630             sub pp_bless{
631             my($new_package)=@_;
632             $::PDLOBJ = $new_package;
633             }
634              
635             # sub to call to set the import list from core on the 'Use Core' line in the .pm file.
636             # set to '()' to not import anything from Core, or 'qw/ barf /' to import barf.
637             sub pp_core_importList{
638             $::PDLCOREIMPORT = $_[0];
639             }
640              
641             sub printxs {
642             shift;
643             $::PDLXS .= join'',@_;
644             }
645              
646             sub pp_addxs {
647             PDL::PP->printxs("\nMODULE = $::PDLMOD PACKAGE = $::CALLPACK\n\n",
648             @_,
649             "\nMODULE = $::PDLMOD PACKAGE = $::PDLOBJ PREFIX=pdl_run_\n\n");
650             }
651              
652             # inserts #line directives into source text. Use like this:
653             # ...
654             # FirstKey => ...,
655             # Code => pp_line_numbers(__LINE__, $x . $y . $c),
656             # OtherKey => ...
657             sub pp_line_numbers {
658             _pp_line_number_file((caller)[1], @_);
659             }
660             sub _pp_line_number_file {
661             my ($filename, $line, $string) = @_;
662             confess "pp_line_numbers called with undef" if !defined $string;
663             # The line needs to be incremented by one for the bookkeeping to work
664             $line++;
665             $filename = 'lib/PDL/PP.pm' if $filename eq __FILE__;
666             $filename =~ s/\\/\\\\/g; # Escape backslashes
667             my @to_return = "\nPDL_LINENO_START $line \"$filename\"\n";
668             # Look for broadcastloops and loops and add # line directives
669             foreach (split (/\n/, $string)) {
670             # Always add the current line.
671             push @to_return, "$_\n";
672             # If we need to add a # line directive, do so after incrementing
673             $line++;
674             if (/%\{/ or /%}/) {
675             push @to_return, "PDL_LINENO_END\n";
676             push @to_return, "PDL_LINENO_START $line \"$filename\"\n";
677             }
678             }
679             push @to_return, "PDL_LINENO_END\n";
680             return join('', @to_return);
681             }
682             my $LINE_RE = qr/^(\s*)PDL_LINENO_(?:START (\S+) "(.*)"|(END))$/;
683             sub _pp_linenumber_fill {
684             local $_; # else get "Modification of a read-only value attempted"
685             my ($file, $text) = @_;
686             my (@stack, @to_return) = [1, $file];
687             my @lines = split /\n/, $text;
688             REALLINE: while (defined($_ = shift @lines)) {
689             $_->[0]++ for @stack;
690             push(@to_return, $_), next if !/$LINE_RE/;
691             my ($ci, $new_line, $new_file, $is_end) = ($1, $2, $3, $4);
692             if (!$is_end) {
693             push @stack, [$new_line-1, $new_file];
694             push @to_return, qq{$ci#line @{[$stack[-1][0]+1]} "$stack[-1][1]"} if @lines;
695             next REALLINE;
696             }
697             @stack = [$stack[0][0], $file]; # as soon as any block is left, line numbers for outer blocks become meaningless
698             my ($seen_empty, $empty_first, $last_ci, @last_dir) = (0, undef, $ci); # list=(line, file)
699             LINE: while (1) {
700             last REALLINE if !@lines;
701             if (!length $lines[0] && $lines[1] !~ /^=/) {
702             $seen_empty = 1;
703             shift @lines;
704             next LINE;
705             }
706             if ($lines[0] =~ /$LINE_RE/) { # directive
707             ($last_ci, @last_dir) = ($1, !$4 ? ($2, $3) : ());
708             $empty_first //= $seen_empty;
709             shift @lines;
710             next LINE;
711             } else { # substantive
712             push @stack, \@last_dir if @last_dir;
713             push(@to_return, ''), $stack[0][0]++ if $seen_empty and $empty_first;
714             push @to_return, qq{$last_ci#line $stack[-1][0] "$stack[-1][1]"};
715             push(@to_return, ''), $stack[0][0]++ if $seen_empty and !$empty_first;
716             last LINE;
717             }
718             }
719             }
720             join '', map "$_\n", @to_return;
721             }
722              
723             sub _file_same {
724             my ($from_text, $to_file) = @_;
725             require File::Map;
726             File::Map::map_file(my $to_map, $to_file, '<');
727             s/^[^\n]*#line[^\n]*?\n//gm for $from_text, (my $to_text = $to_map);
728             $from_text eq $to_text;
729             }
730             sub _write_file {
731             my ($file, $text) = @_;
732             $text = _pp_linenumber_fill($file, $text);
733             return if -f $file && _file_same($text, $file);
734             open my $fh, '>', $file or confess "open $file: $!";
735             binmode $fh; # to guarantee length will be same for same contents
736             print $fh $text;
737             }
738              
739             sub printxsc {
740             (undef, my $file) = (shift, shift);
741             my $text = join '',@_;
742             if (defined $file) {
743             (my $mod_underscores = $::PDLMOD) =~ s#::#_#g;
744             $text = join '', sprintf($PDL::PP::header_c, $0, $mod_underscores), $::PDLXSC_header//'', $text;
745             _write_file($file, $text);
746             } else {
747             $::PDLXSC .= $text;
748             }
749             }
750              
751             sub pp_done {
752             return if $PDL::PP::done; # do only once!
753             $PDL::PP::done = 1;
754             print "DONE!\n" if $::PP_VERBOSE;
755             print "Inline running PDL::PP version $PDL::PP::VERSION...\n" if nopm();
756             require PDL::Core::Dev;
757             my $pdl_boot = PDL::Core::Dev::PDL_BOOT('PDL', $::PDLMOD);
758             my $user_boot = $::PDLXSBOOT//'';
759             $user_boot =~ s/^\s*(.*?)\n*$/ $1\n/ if $user_boot;
760             $user_boot .= <
761             char *package = "$::PDLOBJ";
762             HV* stash = gv_stashpvn(package, strlen(package), TRUE);
763             char *meths[] = {@{[join ',', map qq{"$_"}, @::PDL_LVALUE_SUBS]},NULL}, **methsptr = meths;
764             for (; *methsptr; methsptr++) {
765             SV **meth = hv_fetch(stash, *methsptr, strlen(*methsptr), 0);
766             if (!meth) croak("No found method '%s' in '%s'", *methsptr, package);
767             CV *cv = GvCV(*meth);
768             if (!cv) croak("No found CV for '%s' in '%s'", *methsptr, package);
769             CvLVALUE_on(cv);
770             }
771             EOF
772             (my $mod_underscores = $::PDLMOD) =~ s#::#_#g;
773             my $text = join '',
774             sprintf($PDL::PP::header_c, $0, $mod_underscores),
775             $::PDLXSC//'',
776             $PDL::PP::macros_xs,
777             sprintf($PDL::PP::header_xs, $::PDLMOD, $::PDLOBJ),
778             $::PDLXS, "\n",
779             $PDL::PP::header_xsboot, $pdl_boot, $user_boot;
780             _write_file("$::PDLBASE.xs", $text);
781             return if nopm;
782             $::PDLPMISA = "'".join("','",@::PDLPMISA)."'";
783             $::PDLBEGIN = "BEGIN {\n$::PDLBEGIN\n}"
784             unless $::PDLBEGIN =~ /^\s*$/;
785             $::PDLMODVERSION //= '';
786             $::FUNCSPOD = $::DOCUMENTED ? "\n\n=head1 FUNCTIONS\n\n=cut\n\n" : '';
787             _write_file("$::PDLBASE.pm", join "\n\n", <
788             #
789             # GENERATED WITH PDL::PP from $0! Don't modify!
790             #
791             package $::PDLPACK;
792              
793             our \@EXPORT_OK = qw($::PDLPMROUT);
794             our %EXPORT_TAGS = (Func=>\\\@EXPORT_OK);
795              
796             use PDL::Core$::PDLCOREIMPORT;
797             use PDL::Exporter;
798             use DynaLoader;
799              
800             $::PDL_IFBEGINWRAP[0]
801             $::PDLVERSIONSET
802             our \@ISA = ( $::PDLPMISA );
803             push \@PDL::Core::PP, __PACKAGE__;
804             bootstrap $::PDLMOD $::PDLMODVERSION;
805             $::PDL_IFBEGINWRAP[-1]
806             $::PDLOVERLOAD
807             EOF
808             } # end pp_done
809              
810             sub _pp_parsename {
811             my ($name) = @_;
812             # See if the 'name' is multiline, in which case we extract the
813             # name and add the FullDoc field
814             return ($name, undef) if $name !~ /\n/;
815             my $fulldoc = $name;
816             # See if the very first thing is a word. That is going to be the
817             # name of the function under consideration
818             if ($fulldoc =~ s/^(\w+)//) {
819             $name = $1;
820             } elsif ($fulldoc =~ /=head2 (\w+)/) {
821             $name = $1;
822             } else {
823             croak('Unable to extract name');
824             }
825             ($name, $fulldoc);
826             }
827              
828             sub pp_def {
829             require PDL::Core::Dev;
830             require PDL::Types;
831             require PDL::PP::PdlParObj;
832             require PDL::PP::Signature;
833             require PDL::PP::Dims;
834             require PDL::PP::CType;
835             require PDL::PP::PDLCode;
836             PDL::PP::load_deftable() if !$PDL::PP::deftbl;
837             my($name,%obj) = @_;
838             print "*** Entering pp_def for $name\n" if $::PP_VERBOSE;
839             ($name, my $fulldoc) = _pp_parsename($name);
840             $obj{FullDoc} = $fulldoc if defined $fulldoc;
841             $obj{Name} = $name;
842             croak("ERROR: pp_def=$name given empty GenericTypes!\n")
843             if exists $obj{GenericTypes} and !@{ $obj{GenericTypes} || [] };
844             foreach my $rule (@$PDL::PP::deftbl) {
845             $rule->apply(\%obj);
846             }
847             print "Result of translate for $name:\n" . Dumper(\%obj) . "\n"
848             if exists $obj{Dump} and $obj{Dump} and $::PP_VERBOSE;
849              
850             croak("ERROR: No FreeFunc for pp_def=$name!\n")
851             unless exists $obj{FreeFunc};
852              
853             my $ctext = join("\n\n",grep $_, @obj{qw(
854             CHeader StructDecl RedoDimsFunc
855             ReadDataFunc WriteBackDataFunc
856             FreeFunc
857             VTableDef RunFunc
858             )});
859             if ($::PDLMULTI_C) {
860             PDL::PP->printxsc(undef, "$obj{RunFuncHdr};\n");
861             PDL::PP->printxsc($::PDLMULTI_C_PREFIX."pp-$obj{Name}.c", $ctext);
862             } else {
863             PDL::PP->printxsc(undef, $ctext);
864             }
865             PDL::PP->printxs($obj{NewXSCode});
866             pp_add_boot($obj{BootSetNewXS}) if $obj{BootSetNewXS};
867             PDL::PP->pp_add_exported($name) unless $obj{NoExport};
868             PDL::PP::_pp_addpm_nolineno("\n".$obj{PdlDoc}."\n") if $obj{PdlDoc};
869             PDL::PP::_pp_addpm_nolineno($obj{PMCode}) if defined $obj{PMCode};
870             PDL::PP::_pp_addpm_nolineno($obj{PMFunc}."\n") if defined $obj{PMFunc};
871              
872             print "*** Leaving pp_def for $name\n" if $::PP_VERBOSE;
873             \%obj;
874             }
875              
876             # marks this module as deprecated. This handles the user warnings, and adds a
877             # notice into the documentation. Can take a {infavor => "newmodule"} option
878             sub pp_deprecate_module
879             {
880             my $options;
881             if( ref $_[0] eq 'HASH' ) { $options = shift; }
882             else { $options = { @_ }; }
883              
884             my $infavor;
885              
886             if( $options && ref $options eq 'HASH' && $options->{infavor} )
887             {
888             $infavor = $options->{infavor};
889             }
890              
891             my $mod = $::PDLMOD;
892             my $envvar = 'PDL_SUPPRESS_DEPRECATION_WARNING__' . uc $mod;
893             $envvar =~ s/::/_/g;
894              
895             my $warning_main =
896             "$mod is deprecated.";
897             $warning_main .=
898             " Please use $infavor instead." if $infavor;
899              
900             my $warning_suppression_runtime =
901             "This module will be removed in the future; please update your code.\n" .
902             "Set the environment variable $envvar\n" .
903             "to suppress this warning\n";
904              
905             my $warning_suppression_pod =
906             "A warning will be generated at runtime upon a C of this module\n" .
907             "This warning can be suppressed by setting the $envvar\n" .
908             "environment variable\n";
909              
910             my $deprecation_notice = <
911             XXX=head1 DEPRECATION NOTICE
912              
913             $warning_main
914             $warning_suppression_pod
915              
916             XXX=cut
917              
918             EOF
919             $deprecation_notice =~ s/^XXX=/=/gms;
920             _pp_addpm_nolineno( {At => 'Top'}, $deprecation_notice );
921              
922             _pp_addpm_nolineno {At => 'Top'}, <
923             warn "$warning_main\n$warning_suppression_runtime" unless \$ENV{$envvar};
924             EOF
925             }
926              
927             use Carp;
928             $SIG{__DIE__} = \&Carp::confess if $::PP_VERBOSE;
929              
930             my $typemap_obj;
931             sub _load_typemap {
932             require ExtUtils::Typemaps;
933             require PDL::Core::Dev;
934             # according to MM_Unix 'privlibexp' is the right directory
935             # seems to work even on OS X (where installprivlib breaks things)
936             my $_rootdir = $Config{privlibexp}.'/ExtUtils/';
937             # First the system typemaps..
938             my @tm = ($_rootdir.'../../../../lib/ExtUtils/typemap',
939             $_rootdir.'../../../lib/ExtUtils/typemap',
940             $_rootdir.'../../lib/ExtUtils/typemap',
941             $_rootdir.'../../../typemap',
942             $_rootdir.'../../typemap', $_rootdir.'../typemap',
943             $_rootdir.'typemap');
944             push @tm, &PDL::Core::Dev::PDL_TYPEMAP, '../../typemap', '../typemap', 'typemap';
945             carp "**CRITICAL** PP found no typemaps in (@tm)"
946             unless my @typemaps = grep -f $_ && -T _, @tm;
947             $typemap_obj = ExtUtils::Typemaps->new;
948             $typemap_obj->merge(file => $_, replace => 1) for @typemaps;
949             $typemap_obj;
950             }
951             sub typemap {
952             my ($type, $method) = @_;
953             $typemap_obj ||= _load_typemap();
954             $type=ExtUtils::Typemaps::tidy_type($type);
955             my $inputmap = $typemap_obj->$method(ctype => $type);
956             die "The type =$type= does not have a typemap entry!\n" unless $inputmap;
957             ($inputmap->code, $type);
958             }
959             sub typemap_eval { # lifted from ExtUtils::ParseXS::Eval, ignoring eg $ALIAS
960             my ($code, $varhash) = @_;
961             my ($var, $type, $num, $init, $pname, $arg, $ntype, $argoff, $subtype)
962             = @$varhash{qw(var type num init pname arg ntype argoff subtype)};
963             my $ALIAS;
964             my $rv = eval qq(qq\a$code\a);
965             die $@ if $@;
966             $rv;
967             }
968              
969             sub pp_add_typemaps {
970             confess "Usage: pp_add_typemaps([string|file|typemap]=>\$arg)" if @_ != 2;
971             $typemap_obj ||= _load_typemap();
972             my $new_obj = $_[0] eq 'typemap' ? $_[1] : ExtUtils::Typemaps->new(@_);
973             pp_addxs($new_obj->as_embedded_typemap);
974             $typemap_obj->merge(typemap => $new_obj, replace => 1);
975             }
976              
977             sub make_xs_code {
978             my($xscode_before,$xscode_after,$str,
979             $xs_c_headers,
980             @bits) = @_;
981             my($boot,$prelude);
982             if($xs_c_headers) {
983             $prelude = join '', $xs_c_headers->[0], @bits, $xs_c_headers->[1];
984             $boot = $xs_c_headers->[2];
985             $str .= "\n";
986             } else {
987             my $xscode = join '', @bits;
988             $str .= "$xscode_before\n$xscode$xscode_after\n";
989             }
990             $str =~ s/(\s*\n)+/\n/g;
991             ($str,$boot,$prelude)
992             }
993              
994             sub indent($$) {
995             my ($ind, $text) = @_;
996             return $text if !length $text or !$ind;
997             $ind = ' ' x $ind;
998             $text =~ s/^(.+)$/$ind$1/mg;
999             return $text;
1000             }
1001              
1002             # This subroutine generates the XS code needed to call the perl 'initialize'
1003             # routine in order to create new output PDLs
1004             sub callPerlInit {
1005             my ($sv) = @_;
1006             confess "callPerlInit error: \$sv must be defined" if !defined $sv;
1007             "PDL_XS_PERLINIT_initsv($sv)";
1008             }
1009              
1010             sub callTypemap {
1011             my ($x, $ptype, $pname) = @_;
1012             my ($setter, $type) = typemap($ptype, 'get_inputmap');
1013             (my $ntype = $type) =~ s:\s+::g; $ntype =~ s:\*:Ptr:g;
1014             my $ret = typemap_eval($setter, {var=>$x, type=>$type, arg=>("${x}_SV"),
1015             pname=>$pname, ntype=>$ntype});
1016             $ret =~ s/^\s*(.*?)\s*$/$1/g;
1017             $ret =~ s/\s*\n\s*/ /g;
1018             $ret;
1019             }
1020              
1021             ###########################################################
1022             # Name : extract_signature_from_fulldoc
1023             # Usage : $sig = extract_signature_from_fulldoc($fulldoc)
1024             # Purpose : pull out the signature from the fulldoc string
1025             # Returns : whatever is in parentheses in the signature, or undef
1026             # Parameters : $fulldoc
1027             # Throws : never
1028             # Notes : the signature must have the following form:
1029             # :
1030             # : =for sig
1031             # :
1032             # : Signature: (
1033             # : be multiline>)
1034             # :
1035             # :
1036             # : The two spaces before "Signature" are required, as are
1037             # : the parentheses.
1038             sub extract_signature_from_fulldoc {
1039             my $fulldoc = shift;
1040             if ($fulldoc =~ /=for sig\n\n Signature: \(([^\n]*)\n/g) {
1041             # Extract the signature and remove the final parenthesis
1042             my $sig = $1;
1043             $sig .= $1 while $fulldoc =~ /\G\h+([^\n]*)\n/g;
1044             $sig =~ s/\)\s*$//;
1045             return $sig;
1046             }
1047             return;
1048             }
1049              
1050             # function to be run by real pp_def so fake pp_def can do without other modules
1051             sub load_deftable {
1052             # Build the valid-types regex and valid Pars argument only once. These are
1053             # also used in PDL::PP::PdlParObj, which is why they are globally available.
1054             my $pars_re = $PDL::PP::PdlParObj::pars_re;
1055              
1056             # Set up the rules for translating the pp_def contents.
1057             #
1058             $PDL::PP::deftbl =
1059             [
1060             PDL::PP::Rule->new(
1061             [qw(RedoDims EquivCPOffsCode HandleBad P2Child TwoWay)],
1062             ["Identity"],
1063             "something to do with dataflow between CHILD & PARENT, I think.",
1064             sub {
1065             (PDL::PP::pp_line_numbers(__LINE__-1, '
1066             int i;
1067             $SETNDIMS($PDL(PARENT)->ndims);
1068             for(i=0; i<$PDL(CHILD)->ndims; i++) {
1069             $PDL(CHILD)->dims[i] = $PDL(PARENT)->dims[i];
1070             }
1071             $SETDIMS();
1072             $SETDELTABROADCASTIDS(0);
1073             $PRIV(dims_redone) = 1;
1074             '),
1075             PDL::PP::pp_line_numbers(__LINE__,
1076             'PDL_Indx i;
1077             for(i=0; i<$PDL(CHILD)->nvals; i++) {
1078             $EQUIVCPOFFS(i,i);
1079             }'),
1080             1, 1, 1);
1081             }),
1082              
1083             # used as a flag for many of the routines
1084             # ie should we bother with bad values for this routine?
1085             # 1 - yes,
1086             # 0 - no, maybe issue a warning
1087             PDL::PP::Rule->new("BadFlag", "HandleBad?",
1088             "Sets BadFlag based upon HandleBad key",
1089             sub { $_[0] }),
1090              
1091             ####################
1092             # FullDoc Handling #
1093             ####################
1094              
1095             # Error processing: does FullDoc contain BadDoc, yet BadDoc specified?
1096             PDL::PP::Rule::Croak->new(['FullDoc', 'BadDoc'],
1097             'Cannot have both FullDoc and BadDoc defined'),
1098             PDL::PP::Rule::Croak->new(['FullDoc', 'Doc'],
1099             'Cannot have both FullDoc and Doc defined'),
1100             # Note: no error processing on Pars; it's OK for the docs to gloss over
1101             # the details.
1102              
1103             # Add the Pars section based on the signature of the FullDoc if the Pars
1104             # section doesn't already exist
1105             PDL::PP::Rule->new('Pars', 'FullDoc',
1106             'Sets the Pars from the FullDoc if Pars is not explicitly specified',
1107             # Purpose : extract the Pars from the signature from the fulldoc string,
1108             # : the part of the signature that specifies the ndarrays
1109             # Returns : a string appropriate for the Pars key
1110             # Parameters : $fulldoc
1111             # Throws : if there is no signature
1112             # : if there is no extractable Pars section
1113             # : if some PDL arguments come after the OtherPars arguments start
1114             # Notes : This is meant to be used directly in a Rule. Therefore, it
1115             # : is only called if the Pars key does not yet exist, so if it
1116             # : is not possible to extract the Pars section, it dies.
1117             sub {
1118             my $fulldoc = shift;
1119             # Get the signature or die
1120             my $sig = extract_signature_from_fulldoc($fulldoc)
1121             or confess('No Pars specified and none could be extracted from FullDoc');
1122             # Everything is semicolon-delimited
1123             my @args = split /\s*;\s*/, $sig;
1124             my @pars;
1125             my $switched_to_other_pars = 0;
1126             for my $arg (@args) {
1127             confess('All PDL args must come before other pars in FullDoc signature')
1128             if $switched_to_other_pars and $arg =~ $pars_re;
1129             if ($arg =~ $pars_re) {
1130             push @pars, $arg;
1131             } else {
1132             $switched_to_other_pars = 1;
1133             }
1134             }
1135             # Make sure there's something there
1136             confess('FullDoc signature contains no PDL arguments') if @pars == 0;
1137             # All done!
1138             return join('; ', @pars);
1139             }
1140             ),
1141             PDL::PP::Rule->new('OtherPars', 'FullDoc',
1142             'Sets the OtherPars from the FullDoc if OtherPars is not explicitly specified',
1143             # Purpose : extract the OtherPars from the signature from the fulldoc
1144             # : string, the part of the signature that specifies non-ndarray
1145             # : arguments
1146             # Returns : a string appropriate for the OtherPars key
1147             # Parameters : $fulldoc
1148             # Throws : if some OtherPars arguments come before the last PDL argument
1149             # Notes : This is meant to be used directly in a Rule. Therefore, it
1150             # : is only called if the OtherPars key does not yet exist.
1151             sub {
1152             my $fulldoc = shift;
1153             # Get the signature or do not set
1154             my $sig = extract_signature_from_fulldoc($fulldoc)
1155             or return 'DO NOT SET!!';
1156             # Everything is semicolon-delimited
1157             my @args = split /\s*;\s*/, $sig;
1158             my @otherpars;
1159             for my $arg (@args) {
1160             confess('All PDL args must come before other pars in FullDoc signature')
1161             if @otherpars > 0 and $arg =~ $pars_re;
1162             if ($arg !~ $pars_re) {
1163             push @otherpars, $arg;
1164             }
1165             }
1166             # All done!
1167             return 'DO NOT SET!!'if @otherpars == 0;
1168             return join('; ', @otherpars);
1169             }
1170             ),
1171              
1172             ################################
1173             # Other Documentation Handling #
1174             ################################
1175              
1176             # no docs by default
1177             PDL::PP::Rule::Returns->new("Doc", [], 'Sets the default doc string',
1178             "\n=for ref\n\ninfo not available\n"),
1179              
1180             PDL::PP::Rule->new("BadDoc", [qw(BadFlag Name)],
1181             'Sets the default documentation for handling of bad values',
1182             sub {
1183             my ($bf, $name) = @_;
1184             my $str;
1185             if ( not defined($bf) ) {
1186             $str = "C<$name> does not process bad values.\n";
1187             } elsif ( $bf ) {
1188             $str = "C<$name> processes bad values.\n";
1189             } else {
1190             $str = "C<$name> ignores the bad-value flag of the input ndarrays.\n";
1191             }
1192             $str .= "It will set the bad-value flag of all output ndarrays if " .
1193             "the flag is set for any of the input ndarrays.\n";
1194             }
1195             ),
1196              
1197             # Default: no otherpars
1198             PDL::PP::Rule::Returns::EmptyString->new("OtherPars"),
1199              
1200             # Notes
1201             # Suffix 'NS' means, "Needs Substitution". In other words, the string
1202             # associated with a key that has the suffix "NS" must be run through a
1203             # Substitute
1204             # The substituted version should then replace "NS" with "Subd"
1205             # So: FreeCodeNS -> FreeCodeSubd
1206              
1207             PDL::PP::Rule::Returns->new("StructName", "__privtrans"),
1208             PDL::PP::Rule::Returns->new("ParamStructName", "__params"),
1209              
1210             PDL::PP::Rule->new("HaveBroadcasting","HaveThreading", sub {@_}), # compat
1211             PDL::PP::Rule::Croak->new([qw(P2Child GenericTypes)],
1212             'Cannot have both P2Child and GenericTypes defined'),
1213             PDL::PP::Rule->new([qw(Pars HaveBroadcasting GenericTypes DefaultFlow AllFuncHeader RedoDimsFuncHeader)],
1214             ["P2Child","Name","StructName"],
1215             sub {
1216             my (undef,$name,$sname) = @_;
1217             ("PARENT(); [oca]CHILD();",0,[PDL::Types::ppdefs_all()],1,
1218             "pdl *__it = $sname->pdls[1]; (void) __it;\n",
1219             "PDL->hdr_childcopy($sname); $sname->dims_redone = 1;\n",
1220             );
1221             }),
1222              
1223             # Question: where is ppdefs defined?
1224             # Answer: Core/Types.pm
1225             #
1226             PDL::PP::Rule->new("GenericTypes", [],
1227             'Sets GenericTypes flag to all real types known to PDL::Types',
1228             sub {[PDL::Types::ppdefs()]}),
1229              
1230             PDL::PP::Rule->new("ExtraGenericSwitches", "FTypes",
1231             'Makes ExtraGenericSwitches identical to FTypes if the latter exists and the former does not',
1232             sub {return $_[0]}),
1233             PDL::PP::Rule::Returns->new("ExtraGenericSwitches", [],
1234             'Sets ExtraGenericSwitches to an empty hash if it does not already exist', {}),
1235              
1236             PDL::PP::Rule::InsertName->new("VTableName", 'pdl_%s_vtable'),
1237              
1238             PDL::PP::Rule::Returns->new("Priv", "AffinePriv", 'PDL_Indx incs[$PDL(CHILD)->ndims];PDL_Indx offs; '),
1239             PDL::PP::Rule::Returns->new("IsAffineFlag", "AffinePriv", "PDL_ITRANS_ISAFFINE"),
1240             PDL::PP::Rule::Returns::Zero->new("IsAffineFlag"),
1241             PDL::PP::Rule::Returns->new("TwoWayFlag", "TwoWay", "PDL_ITRANS_TWOWAY"),
1242             PDL::PP::Rule::Returns::Zero->new("TwoWayFlag"),
1243             PDL::PP::Rule::Returns->new("DefaultFlowFlag", "DefaultFlow", "PDL_ITRANS_DO_DATAFLOW_ANY"),
1244             PDL::PP::Rule::Returns::Zero->new("DefaultFlowFlag"),
1245              
1246             PDL::PP::Rule->new("RedoDims", [qw(EquivPDimExpr EquivDimCheck?)],
1247             sub {
1248             my($pdimexpr,$dimcheck) = @_;
1249             $pdimexpr =~ s/\$CDIM\b/i/g;
1250             ' PDL_Indx i,cor;
1251             '.$dimcheck.'
1252             $SETNDIMS($PDL(PARENT)->ndims);
1253             $DOPRIVALLOC();
1254             $PRIV(offs) = 0;
1255             for(i=0; i<$PDL(CHILD)->ndims; i++) {
1256             cor = '.$pdimexpr.';
1257             $PDL(CHILD)->dims[i] = $PDL(PARENT)->dims[cor];
1258             $PRIV(incs)[i] = $PDL(PARENT)->dimincs[cor];
1259             }
1260             $SETDIMS();
1261             $SETDELTABROADCASTIDS(0);
1262             $PRIV(dims_redone) = 1;
1263             ';
1264             }),
1265              
1266             PDL::PP::Rule->new("Code", "EquivCPOffsCode",
1267             "create Code from EquivCPOffsCode",
1268             sub {
1269             my ($good) = @_;
1270             $good =~ s/
1271             \$EQUIVCPOFFS\(([^()]+),([^()]+)\)
1272             /do { PDL_IF_BAD(if (\$PISBAD(PARENT,[$2]) ) { \$PSETBAD(CHILD,[$1]); } else,) { \$P(CHILD)[$1] = \$P(PARENT)[$2]; } } while (0)/gx;
1273             $good =~ s/
1274             \$EQUIVCPTRUNC\(([^()]+),([^()]+),([^()]+)\)
1275             /do { if (($3) PDL_IF_BAD(|| \$PISBAD(PARENT,[$2]),) ) { PDL_IF_BAD(\$PSETBAD(CHILD,[$1]),\$P(CHILD)[$1] = 0); } else {\$P(CHILD)[$1] = \$P(PARENT)[$2]; } } while (0)/gx;
1276             $good;
1277             }),
1278              
1279             PDL::PP::Rule->new("BackCode", "EquivCPOffsCode",
1280             "create BackCode from EquivCPOffsCode",
1281             # Since PARENT & CHILD need NOT be the same type we cannot just copy
1282             # values from one to the other - we have to check for the presence
1283             # of bad values, hence the expansion for the $bad code
1284             #
1285             # Some operators (notably range) also have an out-of-range flag; they use
1286             # the macro EQUIVCPTRUNC instead of EQUIVCPOFFS.
1287             # $EQUIVCPTRUNC does the same as EQUIVCPOFFS but accepts a
1288             # child-out-of-bounds flag. If the out-of-bounds flag is set, the
1289             # forward code puts BAD/0 into the child, and reverse code refrains
1290             # from copying.
1291             # --CED 27-Jan-2003
1292             sub {
1293             my ($good) = @_;
1294             # parse 'good' code
1295             $good =~ s/
1296             \$EQUIVCPOFFS\(([^()]+),([^()]+)\)
1297             /do { PDL_IF_BAD(if( \$PISBAD(CHILD,[$1]) ) { \$PSETBAD(PARENT,[$2]); } else,) { \$P(PARENT)[$2] = \$P(CHILD)[$1]; } } while (0)/gx;
1298             $good =~ s/
1299             \$EQUIVCPTRUNC\(([^()]+),([^()]+),([^()]+)\)
1300             /do { if (!($3)) { PDL_IF_BAD(if (\$PISBAD(CHILD,[$1]) ) { \$PSETBAD(PARENT,[$2]); } else,) { \$P(PARENT)[$2] = \$P(CHILD)[$1]; } } } while (0)/gx;
1301             $good;
1302             }),
1303              
1304             PDL::PP::Rule::Returns::NULL->new("ReadDataFuncName", "AffinePriv"),
1305             PDL::PP::Rule::Returns::NULL->new("WriteBackDataFuncName", "AffinePriv"),
1306              
1307             PDL::PP::Rule::InsertName->new("NewXSName", '_%s_int'),
1308              
1309             PDL::PP::Rule::Returns::One->new("HaveBroadcasting"),
1310              
1311             PDL::PP::Rule::Returns::EmptyString->new("Priv"),
1312             PDL::PP::Rule->new("PrivObj", [qw(Name Priv)],
1313             sub { PDL::PP::Signature->new('', @_) }),
1314              
1315             # Parameters in the 'a(x,y); [o]b(y)' format, with
1316             # fixed nos of real, unbroadcast-over dims.
1317             # Also "Other pars", the parameters which are usually not pdls.
1318             PDL::PP::Rule->new("SignatureObj", [qw(Pars Name OtherPars OtherParsDefaults? ArgOrder?)],
1319             sub { PDL::PP::Signature->new(@_) }),
1320              
1321             # Compiled representations i.e. what the RunFunc function leaves
1322             # in the params structure. By default, copies of the parameters
1323             # but in many cases (e.g. slice) a benefit can be obtained
1324             # by parsing the string in that function.
1325             # If the user wishes to specify their own MakeComp code and Comp content,
1326             # The next definitions allow this.
1327             PDL::PP::Rule->new("CompObj", [qw(Name OtherPars Comp?)],
1328             sub { PDL::PP::Signature->new('', $_[0], join(';', grep defined() && /[^\s;]/, @_[1..$#_])) }),
1329             PDL::PP::Rule->new("CompStruct", ["CompObj"], sub {$_[0]->getcomp}),
1330              
1331             PDL::PP::Rule->new("InplaceNormalised", [qw(Name SignatureObj Inplace)],
1332             'interpret Inplace and Signature to get input/output',
1333             # Inplace can be supplied several values
1334             # => 1
1335             # assumes fn has an input and output ndarray (eg 'a(); [o] b();')
1336             # => [ 'a' ]
1337             # assumes several input ndarrays in sig, so 'a' labels which
1338             # one is to be marked inplace
1339             # => [ 'a', 'b' ]
1340             # input ndarray is a(), output ndarray is 'b'
1341             # this will set InplaceNormalised to [input,output]
1342             sub {
1343             my ($name, $sig, $arg) = @_;
1344             confess 'Inplace given false value' if !$arg;
1345             confess "Inplace array-ref (@$arg) > 2 elements" if ref($arg) eq "ARRAY" and @$arg > 2;
1346             # find input and output ndarrays
1347             my %is_out = map +($_=>1), my @out = $sig->names_out;
1348             my @in = $sig->names_in;
1349             my $in = @in == 1 ? $in[0] : undef;
1350             my $out = @out == 1 ? $out[0] : undef;
1351             my $noutca = $sig->names_oca;
1352             if (ref($arg) eq "ARRAY" and @$arg) {
1353             $in = $$arg[0];
1354             $out = $$arg[1] if @$arg > 1;
1355             }
1356             confess "ERROR in pp_def($name): Inplace does not know name of input ndarray, options were (@in)"
1357             unless defined $in;
1358             confess "ERROR in pp_def($name): Inplace input ndarray '$in' is actually output"
1359             if $is_out{$in};
1360             confess "ERROR in pp_def($name): Inplace does not know name of output ndarray, options were (@out)"
1361             unless defined $out;
1362             my ($in_obj, $out_obj) = map $sig->objs->{$_}, $in, $out;
1363             confess "ERROR in pp_def($name): Inplace output arg $out not [o]\n" if !$$out_obj{FlagW};
1364             my ($in_inds, $out_inds) = map $_->{IndObjs}, $in_obj, $out_obj;
1365             confess "ERROR in pp_def($name): Inplace args $in and $out different number of dims"
1366             if @$in_inds != @$out_inds;
1367             for my $i (0..$#$in_inds) {
1368             my ($in_ind, $out_ind) = map $_->[$i], $in_inds, $out_inds;
1369             next if grep !defined $_->{Value}, $in_ind, $out_ind;
1370             confess "ERROR in pp_def($name): Inplace Pars $in and $out inds ".join('=',@$in_ind{qw(Name Value)})." and ".join('=',@$out_ind{qw(Name Value)})." not compatible"
1371             if $in_ind->{Value} != $out_ind->{Value};
1372             }
1373             my ($in_flags, $out_flags) = map [sort grep /^FlagType/, keys %$_], $in_obj, $out_obj;
1374             confess "ERROR in pp_def($name): Inplace args $in and $out have different type specifications '@$in_flags' vs '@$out_flags'"
1375             if "@$in_flags" ne "@$out_flags";
1376             confess "ERROR in pp_def($name): Inplace args $in and $out have different type specifications '@$in_obj{@$in_flags}' vs '@$out_obj{@$out_flags}"
1377             if "@$in_obj{@$in_flags}" ne "@$out_obj{@$out_flags}";
1378             confess "ERROR in pp_def($name): Inplace args $in and $out have different type specifications '@{[($in_obj->{Type}//'NONE')]}' vs '@{[($out_obj->{Type}//'NONE')]}'"
1379             if ($in_obj->{Type}//'NONE') ne ($out_obj->{Type}//'NONE');
1380             [$in, $out];
1381             }),
1382             PDL::PP::Rule->new(["InplaceCode"], [qw(InplaceNormalised)],
1383             'code to implement working inplace',
1384             # insert code, after the autogenerated xs argument processing code
1385             # produced by VarArgsXSHdr and AFTER any in HdrCode
1386             # - this code flags the routine as working inplace,
1387             sub {
1388             my ($arg) = @_;
1389             my ($in, $out) = @$arg;
1390             " PDL_XS_INPLACE($in, $out)\n";
1391             }),
1392             PDL::PP::Rule::Returns::EmptyString->new("InplaceCode", []),
1393             PDL::PP::Rule->new("InplaceDocValues",
1394             [qw(Name SignatureObj InplaceNormalised NoExport?)],
1395             'doc describing usage inplace',
1396             sub {
1397             my ($name, $sig, $inplace, $noexport) = @_;
1398             my @args = @{ $sig->args_callorder };
1399             my %inplace_involved = map +($_=>1), my ($in, $out) = @$inplace;
1400             my $meth_call = $args[0] eq $in;
1401             @args = grep !$inplace_involved{$_}, @args;
1402             my @vals = !$meth_call ? () : [
1403             "\$$in->inplace->$name".(
1404             !@args ? '' : "(@{[join ', ', map qq{\$$_}, @args]})"
1405             ).";", []
1406             ];
1407             my $prefix = $noexport ? "$::PDLOBJ\::" : "";
1408             push @vals, [ "$prefix$name(\$$in->inplace".(
1409             !@args ? '' : ", @{[join ', ', map qq{\$$_}, @args]}"
1410             ).");", []];
1411             $vals[0][1] = ["can be used inplace"];
1412             \@vals;
1413             }),
1414             PDL::PP::Rule::Returns->new("InplaceDocValues", []),
1415              
1416             PDL::PP::Rule->new("OverloadDocValues",
1417             [qw(Name SignatureObj Overload Inplace?)],
1418             'implement and doc Perl overloaded operators',
1419             sub {
1420             my ($name, $sig, $ovl, $inplace) = @_;
1421             confess "$name Overload given false value" if !$ovl;
1422             $ovl = [$ovl] if !ref $ovl;
1423             my ($op, $mutator, $bitwise) = @$ovl;
1424             confess "$name Overload trying to define mutator but no inplace"
1425             if $mutator && !$inplace;
1426             my $one_arg = $sig->names_in == 1;
1427             my $ret = "{ package # hide from MetaCPAN\n $::PDLOBJ;\n";
1428             my $bitwise_passon = $bitwise ? '$_[2]?@_[1,0]:@_[0,1]' : '@_';
1429             my $fullname = $::PDLOBJ."::$name";
1430             if ($one_arg) {
1431             $ret .= pp_line_numbers(__LINE__, <
1432             use overload '$op' => sub {
1433             Carp::confess("$fullname: overloaded '$op' given undef")
1434             if grep !defined, \$_[0];
1435             $fullname(\$_[0]);
1436             };
1437             EOF
1438             } else {
1439             $ret .= pp_line_numbers(__LINE__, <
1440             {
1441             my (\$foo, \$overload_sub);
1442             use overload '$op' => \$overload_sub = sub {
1443             Carp::confess("$fullname: overloaded '$op' given undef")
1444             if grep !defined, \@_[0,1];
1445             return $fullname($bitwise_passon) unless ref \$_[1]
1446             && (ref \$_[1] ne '$::PDLOBJ')
1447             && defined(\$foo = overload::Method(\$_[1], '$op'))
1448             && \$foo != \$overload_sub; # recursion guard
1449             goto &\$foo;
1450             };
1451             }
1452             EOF
1453             }
1454             $ret .= pp_line_numbers(__LINE__, <
1455             # in1, in2, out, swap if true
1456             use overload '$op=' => sub {
1457             Carp::confess("$fullname: overloaded '$op=' given undef")
1458             if grep !defined, \@_[0,1];
1459             $fullname(\$_[0]->inplace, \$_[1]); \$_[0]
1460             };
1461             EOF
1462             $::PDLOVERLOAD .= "$ret}\n";
1463             my @args = @{ $sig->args_callorder };
1464             my @outs = $sig->names_out;
1465             confess "$name error in Overload doc: !=1 output (@outs)" if @outs != 1;
1466             my @ins = $sig->names_in;
1467             my $sp = $op !~ /^[a-z]/ ? '' : ' ';
1468             my @vals = ["\$$outs[0] = ".(
1469             $one_arg ? "$op$sp\$$ins[0]" :
1470             $sp ? "$op \$$ins[0], \$$ins[1]" :
1471             "\$$ins[0] $op \$$ins[1]"
1472             ).";",
1473             ["overloads the Perl '$op' operator"]
1474             ];
1475             push @vals, ["\$$ins[0] $op= \$$ins[1];", []] if $mutator && !$one_arg;
1476             \@vals;
1477             }),
1478             PDL::PP::Rule::Returns->new("OverloadDocValues", []),
1479              
1480             PDL::PP::Rule->new([qw(UsageDoc ParamDoc)],
1481             [qw(Name Doc? SignatureObj OtherParsDefaults? ArgOrder?
1482             OverloadDocValues InplaceDocValues ParamDesc? Lvalue? NoExport?
1483             )],
1484             'generate "usage" section of doc',
1485             sub {
1486             my ($name, $doc, $sig, $otherdefaults, $argorder,
1487             $overloadvals, $inplacevals, $paramdesc, $lvalue, $noexport,
1488             ) = @_;
1489             $otherdefaults ||= {};
1490             $paramdesc ||= {};
1491             confess "pp_def($name): non-ref ParamDesc given" if !ref $paramdesc;
1492             my @args = @{ $sig->args_callorder };
1493             my $paramdoc = !keys(%$paramdesc) ? '' : join('',
1494             "=head3 Parameters\n\n=over\n\n", (
1495             map "=item $_\n\n$paramdesc->{$_}\n\n", grep $paramdesc->{$_}, @args,
1496             ), "=back\n\n");
1497             return ('',$paramdoc) if $doc && $doc =~ /^=for usage/m;
1498             my %any_out = map +($_=>1), $sig->names_out_nca, $sig->other_out;
1499             my %outca = map +($_=>1), $sig->names_oca;
1500             my @inargs = grep !$outca{$_}, @args;
1501             my $noptional = grep $any_out{$_} ||
1502             exists $otherdefaults->{$_}, @inargs;
1503             my @argsets;
1504             my $plural = keys(%$otherdefaults) > 1 ? "s" : "";
1505             my $override = !keys(%$otherdefaults) ? '' : " of ".join(", ", map "$_=$otherdefaults->{$_}", grep exists $otherdefaults->{$_}, @args);
1506             if (keys %outca) {
1507             push @argsets, [\@inargs, [ grep $outca{$_}, @args ], []];
1508             } elsif ($argorder) {
1509             my @allouts = grep $any_out{$_} || $outca{$_}, @args;
1510             push @argsets, map [[ @inargs[0..$_] ], \@allouts, []],
1511             ($#inargs-$noptional)..$#inargs-@allouts;
1512             push @argsets, [\@args, [], ['all arguments given']];
1513             unshift @{$argsets[0][2]}, "using default$plural$override" if $override;
1514             } else {
1515             push @argsets, [
1516             [grep !($any_out{$_} || exists $otherdefaults->{$_}), @args],
1517             [grep $any_out{$_}, @args],
1518             ["using default value$plural$override"],
1519             ]
1520             if keys %any_out && keys %$otherdefaults;
1521             push @argsets, [
1522             [grep !$any_out{$_},@args],
1523             [grep $any_out{$_},@args],
1524             [keys %$otherdefaults ? "overriding default$plural" : ()],
1525             ]
1526             if keys %any_out;
1527             push @argsets, [\@args, [], ['all arguments given']];
1528             }
1529             my @invocs = @$overloadvals;
1530             my $prefix = $noexport ? "$::PDLOBJ\::" : "";
1531             push @invocs, map [(!@{$_->[1]} ? '' :
1532             @{$_->[1]} == 1 ? "\$$_->[1][0] = " :
1533             "(".join(", ", map "\$$_", @{$_->[1]}).") = "
1534             )."$prefix$name(".join(", ", map "\$$_", @{$_->[0]}).");",
1535             [@{$_->[2]}]], @argsets;
1536             $argsets[0][2] = ['method call'];
1537             $argsets[$_][2] = [] for 1..$#argsets; # they get the idea
1538             push @invocs, map [(!@{$_->[1]} ? '' :
1539             @{$_->[1]} == 1 ? "\$$_->[1][0] = " :
1540             "(".join(", ", map "\$$_", @{$_->[1]}).") = "
1541             )."\$$_->[0][0]->$name".(
1542             @{$_->[0]} <= 1 ? '' :
1543             "(".join(", ", map "\$$_", @{$_->[0]}[1..$#{$_->[0]}]).")"
1544             ).";",
1545             [@{$_->[2]}]], grep @{$_->[0]}, @argsets;
1546             push @invocs, @$inplacevals;
1547             if ($lvalue) {
1548             my ($first_meth) = grep @{$_->[1]} == 1, @argsets;
1549             push @invocs, [
1550             "\$$first_meth->[0][0]->$name".(
1551             @{$first_meth->[0]} <= 1 ? '' :
1552             "(".join(", ", map "\$$_", @{$first_meth->[0]}[1..$#{$first_meth->[0]}]).")"
1553             )." .= \$data;",
1554             ['usable as lvalue']] if $first_meth;
1555             }
1556             require List::Util;
1557             my $maxlen = List::Util::max(map length($_->[0]), @invocs);
1558             (join('', "\n=for usage\n\n",
1559             (map !@{$_->[1]} ? " $_->[0]\n" : sprintf(" %-${maxlen}s%s\n", $_->[0], " # ".join ", ", @{$_->[1]}), @invocs), "\n\n"),
1560             $paramdoc,
1561             );
1562             }),
1563             PDL::PP::Rule::Returns::EmptyString->new("UsageDoc", []),
1564              
1565             # the docs
1566             PDL::PP::Rule->new("PdlDoc", "FullDoc", sub {
1567             my $fulldoc = shift;
1568             # Append a final cut if it doesn't exist due to heredoc shenanigans
1569             $fulldoc .= "\n\n=cut\n" unless $fulldoc =~ /\n=cut\n*$/;
1570             # Make sure the =head1 FUNCTIONS section gets added
1571             $::DOCUMENTED++;
1572             return $fulldoc;
1573             }
1574             ),
1575             PDL::PP::Rule::Returns::Zero->new("NoPthread"), # assume we can pthread, unless indicated otherwise
1576             PDL::PP::Rule->new("PdlDoc", [qw(
1577             Name Pars OtherPars GenericTypes Doc UsageDoc BadDoc?
1578             HaveBroadcasting NoPthread IsAffineFlag TwoWayFlag DefaultFlowFlag
1579             ParamDoc
1580             )],
1581             sub {
1582             my ($name,$pars,$otherpars,$gentypes,$doc,$usagedoc,$baddoc,
1583             $havebroadcasting, $noPthreadFlag, $affflag, $revflag, $flowflag,
1584             $paramdoc,
1585             ) = @_;
1586             return '' if !defined $doc # Allow explicit non-doc using Doc=>undef
1587             or $doc =~ /^\s*internal\s*$/i;
1588             # If the doc string is one line let's have two for the
1589             # reference card information as well
1590             $doc = "=for ref\n\n".$doc if $doc !~ /\n/;
1591             $::DOCUMENTED++;
1592             # Strip leading whitespace and trailing semicolons and whitespace
1593             $pars =~ s/^\s*(.+[^;])[;\s]*$/$1/;
1594             $otherpars =~ s/^\s*(.+[^;])[;\s]*$/$1/ if $otherpars;
1595             my $sig = "$pars".( $otherpars ? "; $otherpars" : "");
1596             my @typenames = map PDL::Type->new($_)->ioname, @$gentypes;
1597             my @typesigparts = '';
1598             while (@typenames) {
1599             push @typesigparts, '' if length $typesigparts[-1] > 50;
1600             $typesigparts[-1] .= ($typesigparts[-1]&&' ') . shift @typenames;
1601             }
1602             my $typesig = join "\n ", @typesigparts;
1603             $doc =~ s/\n(=cut\s*\n)+(\s*\n)*$/\n/m; # Strip extra =cut's
1604             if ( defined $baddoc ) {
1605             # Strip leading newlines and any =cut markings
1606             $baddoc =~ s/\n(=cut\s*\n)+(\s*\n)*$/\n/m;
1607             $baddoc =~ s/^\n+//;
1608             $baddoc = "\n=for bad\n\n$baddoc";
1609             }
1610             my @misc = $havebroadcasting ? "Broadcasts over its inputs.\n" : "Does not broadcast.\n";
1611             push @misc, "Can't use POSIX threads.\n" if $noPthreadFlag;
1612             push @misc, "Makes L ndarrays.\n" if $affflag;
1613             push @misc, "Creates data-flow".(!$revflag ? "" : " back and forth").
1614             " by default.\n" if $flowflag;
1615             my $miscdocs = join '', grep $_, $paramdoc, @misc, $baddoc;
1616             my $baddoc_function_pod = <<"EOD" ;
1617              
1618             XXX=head2 $name
1619              
1620             XXX=for sig
1621              
1622             Signature: ($sig)
1623             Types: ($typesig)
1624             $usagedoc
1625             $doc
1626              
1627             =pod
1628              
1629             $miscdocs
1630              
1631             XXX=cut
1632              
1633             EOD
1634             $baddoc_function_pod =~ s/^XXX=/=/gms;
1635             $baddoc_function_pod;
1636             }
1637             ),
1638              
1639             PDL::PP::Rule::Returns::EmptyString->new("HdrCode", [],
1640             'Code that will be inserted before the call to the RunFunc'),
1641             PDL::PP::Rule::Returns::EmptyString->new("FtrCode", [],
1642             'Code that will be inserted after the call to the RunFunc'),
1643              
1644             PDL::PP::Rule->new("VarArgsXSHdr",
1645             [qw(Name SignatureObj
1646             OtherParsDefaults? ArgOrder? InplaceNormalised?)],
1647             'XS code to process input arguments based on supplied Pars argument to pp_def; not done if GlobalNew or PMCode supplied',
1648             sub {
1649             my($name,$sig,
1650             $otherdefaults,$argorder,$inplace) = @_;
1651             my @args = @{ $sig->args_callorder };
1652             my %other = map +($_=>1), @{$sig->othernames(1, 1)};
1653             $otherdefaults ||= {};
1654             my $ci = 2; # current indenting
1655             my $optypes = $sig->otherobjs;
1656             my %ptypes = map +($_=>$$optypes{$_} ? $$optypes{$_}->get_decl('', {VarArrays2Ptrs=>1}) : 'pdl *'), @args;
1657             my %out = map +($_=>1), $sig->names_out_nca;
1658             my %outca = map +($_=>1), $sig->names_oca;
1659             my @inargs = grep !$outca{$_}, @args;
1660             my %other_out = map +($_=>1), $sig->other_out;
1661             my $nout = keys(%out) + keys(%other_out);
1662             my $noutca = keys %outca;
1663             my $ntot = @args;
1664             my $nallout = $nout + $noutca;
1665             my $ndefault = keys %$otherdefaults;
1666             my %valid_itemcounts = ((my $nmaxonstack = $ntot - $noutca)=>1);
1667             $valid_itemcounts{my $nin = $nmaxonstack - $nout} = 1;
1668             $valid_itemcounts{my $nin_minus_default = "($nin-$ndefault)"} = 1 if $ndefault;
1669             my $only_one = keys(%valid_itemcounts) == 1;
1670             my $nretval = $argorder ? $nout :
1671             $only_one ? $noutca :
1672             "(items == $nmaxonstack) ? $noutca : $nallout";
1673             my ($cnt, @preinit, @xsargs, %already_read, %name2cnts) = -1;
1674             my @inputdecls = map "PDL_Indx ${_}_count=0;", grep $other{$_} && $optypes->{$_}->is_array, @inargs;
1675             foreach my $x (@inargs) {
1676             if (!$argorder && ($out{$x} || $other_out{$x} || exists $otherdefaults->{$x})) {
1677             last if @xsargs + keys(%out) + $noutca != $ntot;
1678             $argorder = 1; # remaining all output ndarrays, engage
1679             }
1680             $cnt++;
1681             $name2cnts{$x} = [$cnt, $cnt];
1682             $already_read{$x} = 1;
1683             push @xsargs, $x.(!$argorder ? '' :
1684             exists $otherdefaults->{$x} ? "=$otherdefaults->{$x}" :
1685             !$out{$x} ? '' :
1686             $inplace && $x eq $inplace->[1] ? "=$x" :
1687             "=".callPerlInit($x."_SV")
1688             );
1689             push @inputdecls, "$ptypes{$x}$x".($inplace && $x eq $inplace->[1] ? "=NO_INIT" : '');
1690             }
1691             my $shortcnt = my $xs_arg_cnt = $cnt;
1692             foreach my $x (@inargs[$cnt+1..$nmaxonstack-1]) {
1693             $cnt++;
1694             $name2cnts{$x} = [$cnt, undef];
1695             $name2cnts{$x}[1] = ++$shortcnt if !($out{$x} || $other_out{$x});
1696             push @xsargs, "$x=$x";
1697             push @inputdecls, "$ptypes{$x}$x".($other{$x} && !exists $otherdefaults->{$x} ? "; { ".callTypemap($x, $ptypes{$x}, $name)."; }" : "=NO_INIT");
1698             }
1699             push @inputdecls, map "$ptypes{$_}$_=".callPerlInit($_."_SV").";", grep $outca{$_}, @args;
1700             my $defaults_rawcond = $ndefault ? "items == $nin_minus_default" : '';
1701             my $svdecls = join '', map "\n $_",
1702             (map "SV *${_}_SV = ".(
1703             !$name2cnts{$_} ? 'NULL' :
1704             ($argorder || (defined $otherdefaults->{$_} && !$nout)) ? "items > $name2cnts{$_}[1] ? ST($name2cnts{$_}[1]) : ".($other_out{$_} ? "sv_newmortal()" : "NULL") :
1705             $name2cnts{$_}[0] == ($name2cnts{$_}[1]//-1) ? "ST($name2cnts{$_}[0])" :
1706             "(items == $nmaxonstack) ? ST($name2cnts{$_}[0]) : ".
1707             (!defined $name2cnts{$_}[1] ? ($other_out{$_} ? "sv_newmortal()" : "NULL") :
1708             defined $otherdefaults->{$_} ? "!($defaults_rawcond) ? ST($name2cnts{$_}[1]) : ".($other_out{$_} ? "sv_newmortal()" : "NULL") :
1709             "ST($name2cnts{$_}[1])"
1710             )
1711             ).";", (grep !$already_read{$_}, $sig->names_in), $sig->names_out, @{$sig->othernames(1, 1, \%already_read)}),
1712             ;
1713             my $argcode =
1714             indent(2, join '',
1715             (map
1716             "if (!${_}_SV) { $_ = ($otherdefaults->{$_}); } else ".
1717             "{ ".callTypemap($_, $ptypes{$_}, $name)."; }\n",
1718             grep !$argorder && exists $otherdefaults->{$_}, @{$sig->othernames(1, 1)}),
1719             (map callTypemap($_, $ptypes{$_}, $name).";\n", grep !$already_read{$_}, $sig->names_in),
1720             (map +("if (${_}_SV) { ".($argorder ? '' : callTypemap($_, $ptypes{$_}, $name))."; } else ")."$_ = ".callPerlInit($_."_SV").";\n", grep $out{$_} && !$already_read{$_} && !($inplace && $_ eq $inplace->[1]), @args)
1721             );
1722             push @preinit, qq[PDL_XS_PREAMBLE($nretval);] if $nallout;
1723             push @preinit, qq{if (!(@{[join ' || ', map "(items == $_)", sort keys %valid_itemcounts]}))
1724             croak("Usage: ${main::PDLOBJ}::$name(@{[
1725             join ",", map exists $otherdefaults->{$_} ? "$_=$otherdefaults->{$_}" :
1726             $out{$_} || $other_out{$_} ? "[$_]" : $_, @inargs
1727             ]}) (you may leave [outputs] and values with =defaults out of list)");}
1728             unless $only_one || $argorder || ($nmaxonstack == keys(%valid_itemcounts) + $xs_arg_cnt);
1729             my $preamble = @preinit ? qq[\n PREINIT:@{[join "\n ", "", @preinit]}\n INPUT:\n] : '';
1730             join '', qq[
1731             \nvoid
1732             pdl_run_$name(@{[join ', ', @xsargs]})$svdecls
1733             $preamble@{[join "\n ", "", @inputdecls]}
1734             PPCODE:
1735             ], map "$_\n", $argcode;
1736             }),
1737              
1738             # globalnew implies internal usage, not XS
1739             PDL::PP::Rule::Returns->new("VarArgsXSReturn","GlobalNew",undef),
1740             PDL::PP::Rule->new("FixArgsXSOtherOutDeclSV",
1741             ["SignatureObj"],
1742             "Generate XS to declare SVs for output OtherPars",
1743             sub {
1744             my ($sig) = @_;
1745             my $optypes = $sig->otherobjs;
1746             my @args = @{ $sig->allnames(1, 1) };
1747             my %outca = map +($_=>1), $sig->names_oca;
1748             my %other_output = map +($_=>1), my @other_output = ($sig->other_io, $sig->other_out);
1749             my $ci = 2;
1750             my $cnt = 0; my %outother2cnt;
1751             foreach my $x (grep !$outca{$_}, @args) {
1752             $outother2cnt{$x} = $cnt if $other_output{$x};
1753             $cnt++;
1754             }
1755             join "\n", map indent($ci,qq{SV *${_}_SV = ST($outother2cnt{$_});}), @other_output;
1756             }),
1757             PDL::PP::Rule->new("XSOtherOutSet",
1758             [qw(Name SignatureObj)],
1759             "Generate XS to set SVs to output values for OtherPars",
1760             sub {
1761             my ($name, $sig) = @_;
1762             my $clause1 = '';
1763             my @other_output = ($sig->other_io, $sig->other_out);
1764             my $optypes = $sig->otherobjs;
1765             my %ptypes = map +($_=>$$optypes{$_}->get_decl('', {VarArrays2Ptrs=>1})), @other_output;
1766             for my $x (@other_output) {
1767             my ($setter, $type) = typemap($ptypes{$x}, 'get_outputmap');
1768             $setter = typemap_eval($setter, {var=>$x, type=>$type, arg=>"tsv",
1769             pname=>$name});
1770             $clause1 .= <
1771             if (!${x}_SV)
1772             PDL->pdl_barf("Internal error in $name: tried to output to NULL ${x}_SV");
1773             {\n SV *tsv = sv_newmortal();
1774             $setter
1775             sv_setsv(${x}_SV, tsv);\n}
1776             EOF
1777             }
1778             indent(2, $clause1);
1779             }),
1780             PDL::PP::Rule->new("VarArgsXSReturn",
1781             ["SignatureObj"],
1782             "Generate XS trailer to return output variables or leave them as modified input variables",
1783             sub {
1784             my ($sig) = @_;
1785             my $oc = my @outs = $sig->names_out; # output ndarrays in calling order
1786             my @other_outputs = ($sig->other_io, $sig->other_out); # output OtherPars
1787             my $clause1 = join ';', (map "ST($_) = $outs[$_]_SV", 0 .. $#outs),
1788             (map "ST(@{[$_+$oc]}) = $other_outputs[$_]_SV", 0 .. $#other_outputs);
1789             $clause1 ? indent(2,"PDL_XS_RETURN($clause1)\n") : '';
1790             }),
1791              
1792             PDL::PP::Rule->new("NewXSHdr", ["NewXSName","SignatureObj"],
1793             sub {
1794             my($name,$sig) = @_;
1795             my $shortpars = join ',', @{ $sig->allnames(1, 1) };
1796             my $optypes = $sig->otherobjs;
1797             my @counts = map "PDL_Indx ${_}_count=0;", grep $optypes->{$_}->is_array, @{ $sig->othernames(1, 1) };
1798             my $longpars = join "\n", map " $_", @counts, $sig->alldecls(1, 0, 1);
1799             return<
1800             \nvoid
1801             $name($shortpars)
1802             $longpars
1803             END
1804             }),
1805             PDL::PP::Rule::InsertName->new("RunFuncName", 'pdl_run_%s'),
1806             PDL::PP::Rule->new("NewXSCHdrs", ["RunFuncName","SignatureObj","GlobalNew"],
1807             sub {
1808             my($name,$sig,$gname) = @_;
1809             my $longpars = join ",", $sig->alldecls(0, 1);
1810             my $opening = ' pdl_error PDL_err = {0, NULL, 0};';
1811             my $closing = ' return PDL_err;';
1812             return ["pdl_error $name($longpars) {$opening","$closing}",
1813             " PDL->$gname = $name;"];
1814             }),
1815             PDL::PP::Rule->new(["RunFuncCall","RunFuncHdr"],["RunFuncName","SignatureObj"], sub {
1816             my ($func_name,$sig) = @_;
1817             my $shortpars = join ',', map $sig->other_is_output($_)?"&$_":$_, @{ $sig->allnames(0) };
1818             my $longpars = join ",", $sig->alldecls(0, 1);
1819             (indent(2,"PDL->barf_if_error($func_name($shortpars));\n"),
1820             "pdl_error $func_name($longpars)");
1821             }),
1822              
1823             PDL::PP::Rule->new("IgnoreTypesOf", ["FTypes","SignatureObj"], sub {
1824             my ($ftypes, $sig) = @_;
1825             my ($pnames, $pobjs) = ($sig->names_sorted, $sig->objs);
1826             $_->{FlagIgnore} = 1 for grep $ftypes->{$_->{Name}}, @$pobjs{@$pnames};
1827             +{map +($_,1), keys %$ftypes};
1828             }),
1829             PDL::PP::Rule::Returns->new("IgnoreTypesOf", {}),
1830              
1831             PDL::PP::Rule->new("NewXSTypeCoerceNS", ["StructName"],
1832             sub { " PDL_RETERROR(PDL_err, PDL->type_coerce($_[0]));\n" }),
1833             PDL::PP::Rule::Substitute->new("NewXSTypeCoerceSubd", "NewXSTypeCoerceNS"),
1834              
1835             PDL::PP::Rule->new("NewXSRunTrans", ["StructName"], sub {
1836             my($trans) = @_;
1837             " PDL_RETERROR(PDL_err, PDL->make_trans_mutual($trans));\n";
1838             }),
1839              
1840             PDL::PP::Rule->new(["StructDecl","ParamStructType"],
1841             ["CompStruct","Name"],
1842             sub {
1843             my($comp,$name) = @_;
1844             return ('', '') if !$comp;
1845             my $ptype = "pdl_params_$name";
1846             (PDL::PP::pp_line_numbers(__LINE__-1, qq[typedef struct $ptype {\n]).qq[$comp\n} $ptype;],
1847             $ptype);
1848             }),
1849              
1850             do {
1851             sub wrap_vfn {
1852             my (
1853             $code,$rout,$func_header,
1854             $all_func_header,$sname,$pname,$ptype,$extra_args,
1855             ) = @_;
1856             join "", PDL::PP::pp_line_numbers(__LINE__,
1857             qq[pdl_error $rout(pdl_trans *$sname$extra_args) {
1858             pdl_error PDL_err = {0, NULL, 0};]),
1859             ($ptype ? " $ptype *$pname = $sname->params; (void)$pname;\n" : ''),
1860             indent(2, join '', grep $_, $all_func_header, $func_header, $code),
1861             " return PDL_err;\n}";
1862             }
1863             sub make_vfn_args {
1864             my ($which, $extra_args) = @_;
1865             ("${which}Func",
1866             ["${which}CodeSubd","${which}FuncName","${which}FuncHeader?",
1867             qw(AllFuncHeader? StructName ParamStructName ParamStructType),
1868             ],
1869             sub {$_[1] eq 'NULL' ? '' : wrap_vfn(@_,$extra_args//'')}
1870             );
1871             }
1872             ()},
1873              
1874             PDL::PP::Rule->new("MakeCompOther", [qw(SignatureObj ParamStructName)], sub { $_[0]->getcopy("$_[1]->%s") }),
1875             PDL::PP::Rule->new("MakeCompTotal", [qw(MakeCompOther MakeComp?)], sub { join "\n", grep $_, @_ }),
1876             PDL::PP::Rule::Substitute->new("MakeCompiledReprSubd", "MakeCompTotal"),
1877              
1878             PDL::PP::Rule->new("NewXSSetTransPDLs", ["SignatureObj","StructName"], sub {
1879             my($sig,$trans) = @_;
1880             join '',
1881             map " $trans->pdls[$_->[0]] = $_->[2];\n",
1882             grep !$_->[1], $sig->names_sorted_tuples;
1883             }),
1884             PDL::PP::Rule->new("NewXSExtractTransPDLs", [qw(SignatureObj StructName MakeComp?)], sub {
1885             my($sig,$trans,$makecomp) = @_;
1886             !$makecomp ? '' : join '',
1887             map " $_->[2] = $trans->pdls[$_->[0]];\n",
1888             grep !$_->[1], $sig->names_sorted_tuples;
1889             }),
1890              
1891             (map PDL::PP::Rule::Substitute->new("${_}ReadDataCodeUnparsed", "${_}Code"), '', 'Bad'),
1892             PDL::PP::Rule->new(PDL::PP::Code::make_args(qw(ReadData)),
1893             sub { PDL::PP::Code->new(@_, undef, undef, 1); }),
1894             PDL::PP::Rule::Substitute->new("ReadDataCodeSubd", "ReadDataCodeParsed"),
1895             PDL::PP::Rule::InsertName->new("ReadDataFuncName", 'pdl_%s_readdata'),
1896             PDL::PP::Rule->new(make_vfn_args("ReadData")),
1897              
1898             (map PDL::PP::Rule::Substitute->new("${_}WriteBackDataCodeUnparsed", "${_}BackCode"), '', 'Bad'),
1899             PDL::PP::Rule->new(PDL::PP::Code::make_args(qw(WriteBackData)),
1900             sub { PDL::PP::Code->new(@_, undef, 1, 1); }),
1901             PDL::PP::Rule::Substitute->new("WriteBackDataCodeSubd", "WriteBackDataCodeParsed"),
1902             PDL::PP::Rule::InsertName->new("WriteBackDataFuncName", "BackCode", 'pdl_%s_writebackdata'),
1903             PDL::PP::Rule::Returns::NULL->new("WriteBackDataFuncName", "Code"),
1904             PDL::PP::Rule->new(make_vfn_args("WriteBackData")),
1905              
1906             # CORE21 move this into pdlapi so RedoDims without Code can broadcast
1907             PDL::PP::Rule->new("DefaultRedoDims",
1908             ["StructName"],
1909             sub { "PDL_RETERROR(PDL_err, PDL->redodims_default($_[0]));\n" }),
1910             PDL::PP::Rule->new("DimsSetters",
1911             ["SignatureObj"],
1912             sub { $_[0]->dims_init }),
1913             PDL::PP::Rule->new("RedoDimsFuncName", [qw(Name RedoDims? RedoDimsCode? DimsSetters)],
1914             sub { (scalar grep $_ && /\S/, @_[1..$#_]) ? "pdl_$_[0]_redodims" : 'NULL'}),
1915             PDL::PP::Rule::Returns->new("RedoDimsCode", [],
1916             'Code that can be inserted to set the size of output ndarrays dynamically based on input ndarrays; is parsed',
1917             ''),
1918             (map PDL::PP::Rule::Substitute->new("RedoDims${_}Unparsed", "RedoDims$_"), '', 'Code'),
1919             PDL::PP::Rule->new(PDL::PP::Code::make_args(qw(RedoDims)),
1920             'makes the parsed representation from the supplied RedoDimsCode',
1921             sub { return '' if !$_[0]; PDL::PP::Code->new(@_, 1, undef, 0); }),
1922             PDL::PP::Rule->new("RedoDimsCodeParsed","RedoDimsUnparsed", sub {@_}),
1923             PDL::PP::Rule->new("RedoDims",
1924             ["DimsSetters","RedoDimsCodeParsed","DefaultRedoDims"],
1925             'makes the redodims function from the various bits and pieces',
1926             sub { join "\n", grep $_ && /\S/, @_ }),
1927             PDL::PP::Rule::Substitute->new("RedoDimsCodeSubd", "RedoDims"),
1928             PDL::PP::Rule->new(make_vfn_args("RedoDims")),
1929              
1930             PDL::PP::Rule->new("CompFreeCode", [qw(CompObj CompFreeCodeComp?)],
1931             "Free any OtherPars/Comp stuff, including user-supplied code (which is probably paired with own MakeComp)",
1932             sub {join '', grep defined() && length, $_[0]->getfree("COMP"), @_[1..$#_]},
1933             ),
1934             PDL::PP::Rule->new("NTPrivFreeCode", "PrivObj", sub {$_[0]->getfree("PRIV")}),
1935             PDL::PP::Rule->new("FreeCodeNS",
1936             ["StructName","CompFreeCode","NTPrivFreeCode"],
1937             sub {
1938             (grep $_, @_[1..$#_]) ? "PDL_FREE_CODE($_[0], destroy, $_[1], $_[2])" : ''}),
1939             PDL::PP::Rule::Substitute->new("FreeCodeSubd", "FreeCodeNS"),
1940             PDL::PP::Rule->new("FreeFuncName",
1941             ["FreeCodeSubd","Name"],
1942             sub {$_[0] ? "pdl_$_[1]_free" : 'NULL'}),
1943             PDL::PP::Rule->new(make_vfn_args("Free", ", char destroy")),
1944              
1945             PDL::PP::Rule->new("NewXSCoerceMustNS", "FTypes",
1946             sub {
1947             my($ftypes) = @_;
1948             join '', map
1949             PDL::PP::pp_line_numbers(__LINE__, "$_->datatype = $ftypes->{$_};"),
1950             sort keys %$ftypes;
1951             }),
1952             PDL::PP::Rule::Returns::EmptyString->new("NewXSCoerceMustNS"),
1953             PDL::PP::Rule::Substitute->new("NewXSCoerceMustCompSubd", "NewXSCoerceMustNS"),
1954              
1955             PDL::PP::Rule->new("NewXSStructInit0",
1956             ["StructName","VTableName","ParamStructName","ParamStructType"],
1957             "Rule to create and initialise the private trans structure",
1958             sub {
1959             my( $sname, $vtable, $pname, $ptype ) = @_;
1960             indent(2, <params;\n" : ""));
1961             if (!PDL) return (pdl_error){PDL_EFATAL, "PDL core struct is NULL, can't continue",0};
1962             pdl_trans *$sname = PDL->create_trans(&$vtable);
1963             if (!$sname) return PDL->make_error_simple(PDL_EFATAL, "Couldn't create trans");
1964             EOF
1965             }),
1966              
1967             PDL::PP::Rule->new(["RunFunc"],
1968             ["RunFuncHdr",
1969             "NewXSStructInit0",
1970             "NewXSSetTransPDLs",
1971             "NewXSTypeCoerceSubd",
1972             "NewXSExtractTransPDLs",
1973             "MakeCompiledReprSubd",
1974             "NewXSCoerceMustCompSubd",
1975             "NewXSRunTrans",
1976             ],
1977             "Generate C function with idiomatic arg list to maybe call from XS",
1978             sub {
1979             my ($xs_c_header, @bits) = @_;
1980             my $opening = ' pdl_error PDL_err = {0, NULL, 0};';
1981             my $closing = ' return PDL_err;';
1982             join '', "$xs_c_header {\n$opening\n", @bits, "$closing\n}\n";
1983             }),
1984              
1985             # internal usage, not XS - NewXSCHdrs only set if GlobalNew
1986             PDL::PP::Rule->new(["NewXSCode","BootSetNewXS","NewXSInPrelude"],
1987             ["NewXSHdr", "NewXSCHdrs", "RunFuncCall"],
1988             "Non-varargs XS code when GlobalNew given",
1989             sub {(undef,(make_xs_code(' CODE:','',@_))[1..2])}),
1990             # if PMCode supplied, no var-args stuff
1991             PDL::PP::Rule->new(["NewXSCode","BootSetNewXS","NewXSInPrelude"],
1992             [qw(PMCode NewXSHdr NewXSCHdrs? FixArgsXSOtherOutDeclSV HdrCode RunFuncCall FtrCode XSOtherOutSet)],
1993             "Non-varargs XS code when PMCode given",
1994             sub {make_xs_code(' CODE:','',@_[1..$#_])}),
1995             PDL::PP::Rule->new(["NewXSCode","BootSetNewXS","NewXSInPrelude"],
1996             [qw(VarArgsXSHdr NewXSCHdrs? HdrCode InplaceCode RunFuncCall FtrCode XSOtherOutSet VarArgsXSReturn)],
1997             "Rule to print out XS code when variable argument list XS processing is enabled",
1998             sub {make_xs_code('','',@_)}),
1999              
2000             PDL::PP::Rule->new("VTableDef",
2001             ["VTableName","ParamStructType","RedoDimsFuncName","ReadDataFuncName",
2002             "WriteBackDataFuncName","FreeFuncName",
2003             "SignatureObj","HaveBroadcasting","NoPthread","Name",
2004             "GenericTypes","IsAffineFlag","TwoWayFlag","DefaultFlowFlag",
2005             "BadFlag"],
2006             sub {
2007             my($vname,$ptype,$rdname,$rfname,$wfname,$ffname,
2008             $sig,$havebroadcasting, $noPthreadFlag, $name, $gentypes,
2009             $affflag, $revflag, $flowflag, $badflag) = @_;
2010             my ($pnames, $pobjs) = ($sig->names_sorted, $sig->objs);
2011             my $nparents = 0 + grep !$pobjs->{$_}->{FlagW}, @$pnames;
2012             my $npdls = scalar @$pnames;
2013             my @op_flags;
2014             push @op_flags, 'PDL_TRANS_DO_BROADCAST' if $havebroadcasting;
2015             push @op_flags, 'PDL_TRANS_BADPROCESS' if $badflag;
2016             push @op_flags, 'PDL_TRANS_BADIGNORE' if defined $badflag and !$badflag;
2017             push @op_flags, 'PDL_TRANS_NO_PARALLEL' if $noPthreadFlag;
2018             push @op_flags, 'PDL_TRANS_OUTPUT_OTHERPAR' if $sig->other_any_out;
2019             my $op_flags = join('|', @op_flags) || '0';
2020             my $iflags = join('|', grep $_, $affflag, $revflag, $flowflag) || '0';
2021             my $gentypes_txt = join(", ", (map PDL::Type->new($_)->sym, @$gentypes), '-1');
2022             my @realdims = map 0+@{$_->{IndObjs}}, @$pobjs{@$pnames};
2023             my $realdims = join(", ", @realdims) || '0';
2024             my $parnames = join(",",map qq|"$_"|, @$pnames) || '""';
2025             my $parflags = join(",\n ",map join('|', $_->cflags)||'0', @$pobjs{@$pnames}) || '0';
2026             my $partypes = join(", ", map defined()?$_->sym:-1, map $_->{Type}, @$pobjs{@$pnames}) || '-1';
2027             my $i = 0; my @starts = map { my $ci = $i; $i += $_; $ci } @realdims;
2028             my $realdim_ind_start = join(", ", @starts) || '0';
2029             my @rd_inds = map $_->get_index, map @{$_->{IndObjs}}, @$pobjs{@$pnames};
2030             my $realdim_inds = join(", ", @rd_inds) || '0';
2031             my @indnames = sort $sig->dims_obj->ind_names;
2032             my $indnames = join(",", map qq|"$_"|, @indnames) || '""';
2033             my $sizeof = $ptype ? "sizeof($ptype)" : '0';
2034             <
2035             static pdl_datatypes ${vname}_gentypes[] = { $gentypes_txt };
2036             static PDL_Indx ${vname}_realdims[] = { $realdims };
2037             static char *${vname}_parnames[] = { $parnames };
2038             static short ${vname}_parflags[] = {
2039             $parflags
2040             };
2041             static pdl_datatypes ${vname}_partypes[] = { $partypes };
2042             static PDL_Indx ${vname}_realdims_starts[] = { $realdim_ind_start };
2043             static PDL_Indx ${vname}_realdims_ind_ids[] = { $realdim_inds };
2044             static char *${vname}_indnames[] = { $indnames };
2045             pdl_transvtable $vname = {
2046             $op_flags, $iflags, ${vname}_gentypes, $nparents, $npdls, NULL /*CORE21*/,
2047             ${vname}_realdims, ${vname}_parnames,
2048             ${vname}_parflags, ${vname}_partypes,
2049             ${vname}_realdims_starts, ${vname}_realdims_ind_ids, @{[scalar @rd_inds]},
2050             @{[scalar @indnames]}, ${vname}_indnames,
2051             $rdname, $rfname, $wfname,
2052             $ffname,
2053             $sizeof,"$::PDLMOD\::$name"
2054             };
2055             EOF
2056             }),
2057              
2058             PDL::PP::Rule->new('PMFunc', 'Name',
2059             'Sets PMFunc to default symbol table manipulations',
2060             sub {
2061             my ($name) = @_;
2062             $::PDL_IFBEGINWRAP[0].'*'.$name.' = \&'.$::PDLOBJ.
2063             '::'.$name.";\n".$::PDL_IFBEGINWRAP[1]
2064             }
2065             ),
2066              
2067             PDL::PP::Rule->new([], [qw(Lvalue Name)],
2068             'If Lvalue key, make the XS routine be lvalue with CvLVALUE_on',
2069             sub {
2070             my (undef, $name) = @_;
2071             push @::PDL_LVALUE_SUBS, $name;
2072             ();
2073             }
2074             ),
2075              
2076             ];
2077             }
2078              
2079             1;