File Coverage

blib/lib/XS/Install/FrozenShit/Typemaps.pm
Criterion Covered Total %
statement 128 279 45.8
branch 33 120 27.5
condition 14 47 29.7
subroutine 15 30 50.0
pod 18 19 94.7
total 208 495 42.0


line stmt bran cond sub pod time code
1             package
2             XS::Install::FrozenShit::Typemaps;
3 1     1   14 use 5.006001;
  1         2  
4 1     1   3 use strict;
  1         2  
  1         22  
5 1     1   6 use warnings;
  1         2  
  1         4499  
6             our $VERSION = '3.57';
7              
8             require XS::Install::FrozenShit::ParseXS;
9             require XS::Install::FrozenShit::ParseXS::Constants;
10             require XS::Install::FrozenShit::Typemaps::InputMap;
11             require XS::Install::FrozenShit::Typemaps::OutputMap;
12             require XS::Install::FrozenShit::Typemaps::Type;
13              
14             =head1 NAME
15              
16             XS::Install::FrozenShit::Typemaps - Read/Write/Modify Perl/XS typemap files
17              
18             =head1 SYNOPSIS
19              
20             # read/create file
21             my $typemap = XS::Install::FrozenShit::Typemaps->new(file => 'typemap');
22             # alternatively create an in-memory typemap
23             # $typemap = XS::Install::FrozenShit::Typemaps->new();
24             # alternatively create an in-memory typemap by parsing a string
25             # $typemap = XS::Install::FrozenShit::Typemaps->new(string => $sometypemap);
26              
27             # add a mapping
28             $typemap->add_typemap(ctype => 'NV', xstype => 'T_NV');
29             $typemap->add_inputmap(
30             xstype => 'T_NV', code => '$var = ($type)SvNV($arg);'
31             );
32             $typemap->add_outputmap(
33             xstype => 'T_NV', code => 'sv_setnv($arg, (NV)$var);'
34             );
35             $typemap->add_string(string => $typemapstring);
36             # will be parsed and merged
37              
38             # remove a mapping (same for remove_typemap and remove_outputmap...)
39             $typemap->remove_inputmap(xstype => 'SomeType');
40              
41             # save a typemap to a file
42             $typemap->write(file => 'anotherfile.map');
43              
44             # merge the other typemap into this one
45             $typemap->merge(typemap => $another_typemap);
46              
47             =head1 DESCRIPTION
48              
49             This module can read, modify, create and write Perl XS typemap files. If you don't know
50             what a typemap is, please confer the L and L manuals.
51              
52             The module is not entirely round-trip safe: For example it currently simply strips all comments.
53             The order of entries in the maps is, however, preserved.
54              
55             We check for duplicate entries in the typemap, but do not check for missing
56             C entries for C or C entries since these might be hidden
57             in a different typemap.
58              
59             =head1 METHODS
60              
61             =cut
62              
63             =head2 new
64              
65             Returns a new typemap object. Takes an optional C parameter.
66             If set, the given file will be read. If the file doesn't exist, an empty typemap
67             is returned.
68              
69             Alternatively, if the C parameter is given, the supplied
70             string will be parsed instead of a file.
71              
72             =cut
73              
74             sub new {
75 3     3 1 9 my $class = shift;
76 3         9 my %args = @_;
77              
78 3 50 66     26 if (defined $args{file} and defined $args{string}) {
79 0         0 die("Cannot handle both 'file' and 'string' arguments to constructor");
80             }
81              
82 3         91 my $self = bless {
83             file => undef,
84             %args,
85             typemap_section => [],
86             typemap_lookup => {},
87             input_section => [],
88             input_lookup => {},
89             output_section => [],
90             output_lookup => {},
91             } => $class;
92              
93 3         27 $self->_init();
94              
95 3         20 return $self;
96             }
97              
98             sub _init {
99 3     3   9 my $self = shift;
100 3 50 66     75 if (defined $self->{string}) {
    100          
101 0         0 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
102 0         0 delete $self->{string};
103             }
104             elsif (defined $self->{file} and -e $self->{file}) {
105             open my $fh, '<', $self->{file}
106             or die "Cannot open typemap file '"
107 2 50       241 . $self->{file} . "' for reading: $!";
108 2         16 local $/ = undef;
109 2         112 my $string = <$fh>;
110 2         35 $self->_parse(\$string, $self->{lineno_offset}, $self->{file});
111             }
112             }
113              
114              
115             =head2 file
116              
117             Get/set the file that the typemap is written to when the
118             C method is called.
119              
120             =cut
121              
122             sub file {
123 0 0   0 1 0 $_[0]->{file} = $_[1] if @_ > 1;
124             $_[0]->{file}
125 0         0 }
126              
127             =head2 add_typemap
128              
129             Add a C entry to the typemap.
130              
131             Required named arguments: The C (e.g. C 'double'>)
132             and the C (e.g. C 'T_NV'>).
133              
134             Optional named arguments: C 1> forces removal/replacement of
135             existing C entries of the same C. C 1>
136             triggers a I<"first come first serve"> logic by which new entries that conflict
137             with existing entries are silently ignored.
138              
139             As an alternative to the named parameters usage, you may pass in
140             an C object as first argument, a copy of which will be
141             added to the typemap. In that case, only the C or C named parameters
142             may be used after the object. Example:
143              
144             $map->add_typemap($type_obj, replace => 1);
145              
146             =cut
147              
148             sub add_typemap {
149 134     134 1 226 my $self = shift;
150 134         277 my $type;
151             my %args;
152              
153 134 50       337 if ((@_ % 2) == 1) {
154 134         227 my $orig = shift;
155 134         358 $type = $orig->new();
156 134         420 %args = @_;
157             }
158             else {
159 0         0 %args = @_;
160 0         0 my $ctype = $args{ctype};
161 0 0       0 die("Need ctype argument") if not defined $ctype;
162 0         0 my $xstype = $args{xstype};
163 0 0       0 die("Need xstype argument") if not defined $xstype;
164              
165             $type = XS::Install::FrozenShit::Typemaps::Type->new(
166             xstype => $xstype,
167 0         0 'prototype' => $args{'prototype'},
168             ctype => $ctype,
169             );
170             }
171              
172 134 0 33     416 if ($args{skip} and $args{replace}) {
173 0         0 die("Cannot use both 'skip' and 'replace'");
174             }
175              
176 134 50       305 if ($args{replace}) {
    0          
177 134         418 $self->remove_typemap(ctype => $type->ctype);
178             }
179             elsif ($args{skip}) {
180 0 0       0 return() if exists $self->{typemap_lookup}{$type->ctype};
181             }
182             else {
183 0         0 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
184             }
185              
186             # store
187 134         314 push @{$self->{typemap_section}}, $type;
  134         330  
188             # remember type for lookup, too.
189 134         219 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
  134         500  
190              
191 134         833 return 1;
192             }
193              
194             =head2 add_inputmap
195              
196             Add an C entry to the typemap.
197              
198             Required named arguments:
199             The C (e.g. C 'T_NV'>)
200             and the C to associate with it for input.
201              
202             Optional named arguments: C 1> forces removal/replacement of
203             existing C entries of the same C. C 1>
204             triggers a I<"first come first serve"> logic by which new entries that conflict
205             with existing entries are silently ignored.
206              
207             As an alternative to the named parameters usage, you may pass in
208             an C object as first argument, a copy of which will be
209             added to the typemap. In that case, only the C or C named parameters
210             may be used after the object. Example:
211              
212             $map->add_inputmap($type_obj, replace => 1);
213              
214             =cut
215              
216             sub add_inputmap {
217 100     100 1 180 my $self = shift;
218 100         168 my $input;
219             my %args;
220              
221 100 50       233 if ((@_ % 2) == 1) {
222 100         167 my $orig = shift;
223 100         280 $input = $orig->new();
224 100         314 %args = @_;
225             }
226             else {
227 0         0 %args = @_;
228 0         0 my $xstype = $args{xstype};
229 0 0       0 die("Need xstype argument") if not defined $xstype;
230 0         0 my $code = $args{code};
231 0 0       0 die("Need code argument") if not defined $code;
232              
233 0         0 $input = XS::Install::FrozenShit::Typemaps::InputMap->new(
234             xstype => $xstype,
235             code => $code,
236             );
237             }
238              
239 100 0 33     234 if ($args{skip} and $args{replace}) {
240 0         0 die("Cannot use both 'skip' and 'replace'");
241             }
242              
243 100 50       213 if ($args{replace}) {
    0          
244 100         301 $self->remove_inputmap(xstype => $input->xstype);
245             }
246             elsif ($args{skip}) {
247 0 0       0 return() if exists $self->{input_lookup}{$input->xstype};
248             }
249             else {
250 0         0 $self->validate(inputmap_xstype => $input->xstype);
251             }
252              
253             # store
254 100         172 push @{$self->{input_section}}, $input;
  100         254  
255             # remember type for lookup, too.
256 100         166 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
  100         296  
257              
258 100         337 return 1;
259             }
260              
261             =head2 add_outputmap
262              
263             Add an C entry to the typemap.
264             Works exactly the same as C.
265              
266             =cut
267              
268             sub add_outputmap {
269 100     100 1 174 my $self = shift;
270 100         199 my $output;
271             my %args;
272              
273 100 50       224 if ((@_ % 2) == 1) {
274 100         154 my $orig = shift;
275 100         247 $output = $orig->new();
276 100         247 %args = @_;
277             }
278             else {
279 0         0 %args = @_;
280 0         0 my $xstype = $args{xstype};
281 0 0       0 die("Need xstype argument") if not defined $xstype;
282 0         0 my $code = $args{code};
283 0 0       0 die("Need code argument") if not defined $code;
284              
285 0         0 $output = XS::Install::FrozenShit::Typemaps::OutputMap->new(
286             xstype => $xstype,
287             code => $code,
288             );
289             }
290              
291 100 0 33     265 if ($args{skip} and $args{replace}) {
292 0         0 die("Cannot use both 'skip' and 'replace'");
293             }
294              
295 100 50       223 if ($args{replace}) {
    0          
296 100         253 $self->remove_outputmap(xstype => $output->xstype);
297             }
298             elsif ($args{skip}) {
299 0 0       0 return() if exists $self->{output_lookup}{$output->xstype};
300             }
301             else {
302 0         0 $self->validate(outputmap_xstype => $output->xstype);
303             }
304              
305             # store
306 100         168 push @{$self->{output_section}}, $output;
  100         292  
307             # remember type for lookup, too.
308 100         163 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
  100         270  
309              
310 100         333 return 1;
311             }
312              
313             =head2 add_string
314              
315             Parses a string as a typemap and merge it into the typemap object.
316              
317             Required named argument: C to specify the string to parse.
318              
319             =cut
320              
321             sub add_string {
322 0     0 1 0 my $self = shift;
323 0         0 my %args = @_;
324 0 0       0 die("Need 'string' argument") if not defined $args{string};
325              
326             # no, this is not elegant.
327 0         0 my $other = XS::Install::FrozenShit::Typemaps->new(string => $args{string});
328 0         0 $self->merge(typemap => $other);
329             }
330              
331             =head2 remove_typemap
332              
333             Removes a C entry from the typemap.
334              
335             Required named argument: C to specify the entry to remove from the typemap.
336              
337             Alternatively, you may pass a single C object.
338              
339             =cut
340              
341             sub remove_typemap {
342 134     134 1 240 my $self = shift;
343 134         280 my $ctype;
344 134 50       280 if (@_ > 1) {
345 134         355 my %args = @_;
346 134         272 $ctype = $args{ctype};
347 134 50       330 die("Need ctype argument") if not defined $ctype;
348 134         300 $ctype = tidy_type($ctype);
349             }
350             else {
351 0         0 $ctype = $_[0]->tidy_ctype;
352             }
353              
354 134         454 return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup});
355             }
356              
357             =head2 remove_inputmap
358              
359             Removes an C entry from the typemap.
360              
361             Required named argument: C to specify the entry to remove from the typemap.
362              
363             Alternatively, you may pass a single C object.
364              
365             =cut
366              
367             sub remove_inputmap {
368 100     100 1 161 my $self = shift;
369 100         172 my $xstype;
370 100 50       231 if (@_ > 1) {
371 100         272 my %args = @_;
372 100         192 $xstype = $args{xstype};
373 100 50       268 die("Need xstype argument") if not defined $xstype;
374             }
375             else {
376 0         0 $xstype = $_[0]->xstype;
377             }
378            
379 100         267 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup});
380             }
381              
382             =head2 remove_outputmap
383              
384             Removes an C entry from the typemap.
385              
386             Required named argument: C to specify the entry to remove from the typemap.
387              
388             Alternatively, you may pass a single C object.
389              
390             =cut
391              
392             sub remove_outputmap {
393 100     100 1 228 my $self = shift;
394 100         178 my $xstype;
395 100 50       194 if (@_ > 1) {
396 100         274 my %args = @_;
397 100         218 $xstype = $args{xstype};
398 100 50       290 die("Need xstype argument") if not defined $xstype;
399             }
400             else {
401 0         0 $xstype = $_[0]->xstype;
402             }
403            
404 100         261 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
405             }
406              
407             sub _remove {
408 334     334   603 my $self = shift;
409 334         625 my $rm = shift;
410 334         515 my $array = shift;
411 334         535 my $lookup = shift;
412              
413             # Just fetch the index of the item from the lookup table
414 334         673 my $index = $lookup->{$rm};
415 334 100       1105 return() if not defined $index;
416              
417             # Nuke the item from storage
418 4         14 splice(@$array, $index, 1);
419              
420             # Decrement the storage position of all items thereafter
421 4         67 foreach my $key (keys %$lookup) {
422 228 100       497 if ($lookup->{$key} > $index) {
423 132         212 $lookup->{$key}--;
424             }
425             }
426 4         28 return();
427             }
428              
429             =head2 get_typemap
430              
431             Fetches an entry of the TYPEMAP section of the typemap.
432              
433             Mandatory named arguments: The C of the entry.
434              
435             Returns the C
436             object for the entry if found.
437              
438             =cut
439              
440             sub get_typemap {
441             my $self = shift;
442             die("Need named parameters, got uneven number") if @_ % 2;
443              
444             my %args = @_;
445             my $ctype = $args{ctype};
446             die("Need ctype argument") if not defined $ctype;
447             $ctype = tidy_type($ctype);
448              
449             my $index = $self->{typemap_lookup}{$ctype};
450             return() if not defined $index;
451             return $self->{typemap_section}[$index];
452             }
453              
454             =head2 get_inputmap
455              
456             Fetches an entry of the INPUT section of the
457             typemap.
458              
459             Mandatory named arguments: The C of the
460             entry or the C of the typemap that can be used to find
461             the C. To wit, the following pieces of code
462             are equivalent:
463              
464             my $type = $typemap->get_typemap(ctype => $ctype)
465             my $input_map = $typemap->get_inputmap(xstype => $type->xstype);
466              
467             my $input_map = $typemap->get_inputmap(ctype => $ctype);
468              
469             Returns the C
470             object for the entry if found.
471              
472             =cut
473              
474             sub get_inputmap {
475 4     4 1 9 my $self = shift;
476 4 50       13 die("Need named parameters, got uneven number") if @_ % 2;
477              
478 4         14 my %args = @_;
479 4         9 my $xstype = $args{xstype};
480 4         11 my $ctype = $args{ctype};
481 4 0 33     11 die("Need xstype or ctype argument")
482             if not defined $xstype
483             and not defined $ctype;
484 4 50 33     28 die("Need xstype OR ctype arguments, not both")
485             if defined $xstype and defined $ctype;
486              
487 4 50       10 if (defined $ctype) {
488 0         0 my $tm = $self->get_typemap(ctype => $ctype);
489 0   0     0 $xstype = $tm && $tm->xstype;
490 0 0       0 return() if not defined $xstype;
491             }
492              
493 4         13 my $index = $self->{input_lookup}{$xstype};
494 4 50       13 return() if not defined $index;
495 4         25 return $self->{input_section}[$index];
496             }
497              
498             =head2 get_outputmap
499              
500             Fetches an entry of the OUTPUT section of the
501             typemap.
502              
503             Mandatory named arguments: The C of the
504             entry or the C of the typemap that can be used to
505             resolve the C. (See above for an example.)
506              
507             Returns the C
508             object for the entry if found.
509              
510             =cut
511              
512             sub get_outputmap {
513 4     4 1 9 my $self = shift;
514 4 50       15 die("Need named parameters, got uneven number") if @_ % 2;
515              
516 4         16 my %args = @_;
517 4         11 my $xstype = $args{xstype};
518 4         11 my $ctype = $args{ctype};
519 4 50 66     22 die("Need xstype or ctype argument")
520             if not defined $xstype
521             and not defined $ctype;
522 4 50 66     19 die("Need xstype OR ctype arguments, not both")
523             if defined $xstype and defined $ctype;
524              
525 4 100       12 if (defined $ctype) {
526 2         10 my $tm = $self->get_typemap(ctype => $ctype);
527 2   33     21 $xstype = $tm && $tm->xstype;
528 2 50       8 return() if not defined $xstype;
529             }
530              
531 4         11 my $index = $self->{output_lookup}{$xstype};
532 4 50       11 return() if not defined $index;
533 4         17 return $self->{output_section}[$index];
534             }
535              
536             =head2 write
537              
538             Write the typemap to a file. Optionally takes a C argument. If given, the
539             typemap will be written to the specified file. If not, the typemap is written
540             to the currently stored file name (see L above, this defaults to the file
541             it was read from if any).
542              
543             =cut
544              
545             sub write {
546 0     0 1 0 my $self = shift;
547 0         0 my %args = @_;
548 0 0       0 my $file = defined $args{file} ? $args{file} : $self->file();
549 0 0       0 die("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
550             if not defined $file;
551              
552 0 0       0 open my $fh, '>', $file
553             or die "Cannot open typemap file '$file' for writing: $!";
554 0         0 print $fh $self->as_string();
555 0         0 close $fh;
556             }
557              
558             =head2 as_string
559              
560             Generates and returns the string form of the typemap.
561              
562             =cut
563              
564             sub as_string {
565 0     0 1 0 my $self = shift;
566 0         0 my $typemap = $self->{typemap_section};
567 0         0 my @code;
568 0         0 push @code, "TYPEMAP\n";
569 0         0 foreach my $entry (@$typemap) {
570             # type kind proto
571             # /^(.*?\S)\s+(\S+)\s*($XS::Install::FrozenShit::ParseXS::Constants::PrototypeRegexp*)$/o
572 0 0       0 push @code, $entry->ctype . "\t" . $entry->xstype
573             . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
574             }
575              
576 0         0 my $input = $self->{input_section};
577 0 0       0 if (@$input) {
578 0         0 push @code, "\nINPUT\n";
579 0         0 foreach my $entry (@$input) {
580 0         0 push @code, $entry->xstype, "\n", $entry->code, "\n";
581             }
582             }
583              
584 0         0 my $output = $self->{output_section};
585 0 0       0 if (@$output) {
586 0         0 push @code, "\nOUTPUT\n";
587 0         0 foreach my $entry (@$output) {
588 0         0 push @code, $entry->xstype, "\n", $entry->code, "\n";
589             }
590             }
591 0         0 return join '', @code;
592             }
593              
594             =head2 as_embedded_typemap
595              
596             Generates and returns the string form of the typemap with the
597             appropriate prefix around it for verbatim inclusion into an
598             XS file as an embedded typemap. This will return a string like
599              
600             TYPEMAP: <
601             ... typemap here (see as_string) ...
602             END_OF_TYPEMAP
603              
604             The method takes care not to use a HERE-doc end marker that
605             appears in the typemap string itself.
606              
607             =cut
608              
609             sub as_embedded_typemap {
610 0     0 1 0 my $self = shift;
611 0         0 my $string = $self->as_string;
612              
613 0         0 my @ident_cand = qw(END_TYPEMAP END_OF_TYPEMAP END);
614 0         0 my $icand = 0;
615 0         0 my $cand_suffix = "";
616 0         0 while ($string =~ /^\Q$ident_cand[$icand]$cand_suffix\E\s*$/m) {
617 0         0 $icand++;
618 0 0       0 if ($icand == @ident_cand) {
619 0         0 $icand = 0;
620 0         0 ++$cand_suffix;
621             }
622             }
623              
624 0         0 my $marker = "$ident_cand[$icand]$cand_suffix";
625 0         0 return "TYPEMAP: <<$marker;\n$string\n$marker\n";
626             }
627              
628             =head2 merge
629              
630             Merges a given typemap into the object. Note that a failed merge
631             operation leaves the object in an inconsistent state so clone it if necessary.
632              
633             Mandatory named arguments: Either C $another_typemap_obj>
634             or C $path_to_typemap_file> but not both.
635              
636             Optional arguments: C 1> to force replacement
637             of existing typemap entries without warning or C 1>
638             to skip entries that exist already in the typemap.
639              
640             =cut
641              
642             sub merge {
643             my $self = shift;
644             my %args = @_;
645              
646             if (exists $args{typemap} and exists $args{file}) {
647             die("Need {file} OR {typemap} argument. Not both!");
648             }
649             elsif (not exists $args{typemap} and not exists $args{file}) {
650             die("Need {file} or {typemap} argument!");
651             }
652              
653             my @params;
654             push @params, 'replace' => $args{replace} if exists $args{replace};
655             push @params, 'skip' => $args{skip} if exists $args{skip};
656              
657             my $typemap = $args{typemap};
658             if (not defined $typemap) {
659             $typemap = ref($self)->new(file => $args{file}, @params);
660             }
661              
662             # FIXME breaking encapsulation. Add accessor code.
663             foreach my $entry (@{$typemap->{typemap_section}}) {
664             $self->add_typemap( $entry, @params );
665             }
666              
667             foreach my $entry (@{$typemap->{input_section}}) {
668             $self->add_inputmap( $entry, @params );
669             }
670              
671             foreach my $entry (@{$typemap->{output_section}}) {
672             $self->add_outputmap( $entry, @params );
673             }
674              
675             return 1;
676             }
677              
678             =head2 is_empty
679              
680             Returns a bool indicating whether this typemap is entirely empty.
681              
682             =cut
683              
684             sub is_empty {
685 0     0 1 0 my $self = shift;
686              
687             return @{ $self->{typemap_section} } == 0
688             && @{ $self->{input_section} } == 0
689 0   0     0 && @{ $self->{output_section} } == 0;
690             }
691              
692             =head2 list_mapped_ctypes
693              
694             Returns a list of the C types that are mappable by
695             this typemap object.
696              
697             =cut
698              
699             sub list_mapped_ctypes {
700 0     0 1 0 my $self = shift;
701 0         0 return sort keys %{ $self->{typemap_lookup} };
  0         0  
702             }
703              
704             =head2 _get_typemap_hash
705              
706             Returns a hash mapping the C types to the XS types:
707              
708             {
709             'char **' => 'T_PACKEDARRAY',
710             'bool_t' => 'T_IV',
711             'AV *' => 'T_AVREF',
712             'InputStream' => 'T_IN',
713             'double' => 'T_DOUBLE',
714             # ...
715             }
716              
717             This is documented because it is used by C,
718             but it's not intended for general consumption. May be removed
719             at any time.
720              
721             =cut
722              
723             sub _get_typemap_hash {
724 0     0   0 my $self = shift;
725 0         0 my $lookup = $self->{typemap_lookup};
726 0         0 my $storage = $self->{typemap_section};
727              
728 0         0 my %rv;
729 0         0 foreach my $ctype (keys %$lookup) {
730 0         0 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
731             }
732              
733 0         0 return \%rv;
734             }
735              
736             =head2 _get_inputmap_hash
737              
738             Returns a hash mapping the XS types (identifiers) to the
739             corresponding INPUT code:
740              
741             {
742             'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
743             ',
744             'T_OUT' => ' $var = IoOFP(sv_2io($arg))
745             ',
746             'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
747             # ...
748             }
749              
750             This is documented because it is used by C,
751             but it's not intended for general consumption. May be removed
752             at any time.
753              
754             =cut
755              
756             sub _get_inputmap_hash {
757 0     0   0 my $self = shift;
758 0         0 my $lookup = $self->{input_lookup};
759 0         0 my $storage = $self->{input_section};
760              
761 0         0 my %rv;
762 0         0 foreach my $xstype (keys %$lookup) {
763 0         0 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
764              
765             # Squash trailing whitespace to one line break
766             # This isn't strictly necessary, but makes the output more similar
767             # to the original XS::Install::FrozenShit::ParseXS.
768 0         0 $rv{$xstype} =~ s/\s*\z/\n/;
769             }
770              
771 0         0 return \%rv;
772             }
773              
774              
775             =head2 _get_outputmap_hash
776              
777             Returns a hash mapping the XS types (identifiers) to the
778             corresponding OUTPUT code:
779              
780             {
781             'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
782             $var.context.value().size());
783             ',
784             'T_OUT' => ' {
785             GV *gv = (GV *)sv_newmortal();
786             gv_init_pvn(gv, gv_stashpvs("$Package",1),
787             "__ANONIO__",10,0);
788             if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
789             sv_setsv(
790             $arg,
791             sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))
792             );
793             else
794             $arg = &PL_sv_undef;
795             }
796             ',
797             # ...
798             }
799              
800             This is documented because it is used by C,
801             but it's not intended for general consumption. May be removed
802             at any time.
803              
804             =cut
805              
806             sub _get_outputmap_hash {
807 0     0   0 my $self = shift;
808 0         0 my $lookup = $self->{output_lookup};
809 0         0 my $storage = $self->{output_section};
810              
811 0         0 my %rv;
812 0         0 foreach my $xstype (keys %$lookup) {
813 0         0 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
814              
815             # Squash trailing whitespace to one line break
816             # This isn't strictly necessary, but makes the output more similar
817             # to the original XS::Install::FrozenShit::ParseXS.
818 0         0 $rv{$xstype} =~ s/\s*\z/\n/;
819             }
820              
821 0         0 return \%rv;
822             }
823              
824             =head2 _get_prototype_hash
825              
826             Returns a hash mapping the C types of the typemap to their
827             corresponding prototypes.
828              
829             {
830             'char **' => '$',
831             'bool_t' => '$',
832             'AV *' => '$',
833             'InputStream' => '$',
834             'double' => '$',
835             # ...
836             }
837              
838             This is documented because it is used by C,
839             but it's not intended for general consumption. May be removed
840             at any time.
841              
842             =cut
843              
844             sub _get_prototype_hash {
845 0     0   0 my $self = shift;
846 0         0 my $lookup = $self->{typemap_lookup};
847 0         0 my $storage = $self->{typemap_section};
848              
849 0         0 my %rv;
850 0         0 foreach my $ctype (keys %$lookup) {
851 0   0     0 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
852             }
853              
854 0         0 return \%rv;
855             }
856              
857              
858              
859             # make sure that the provided types wouldn't collide with what's
860             # in the object already.
861             sub validate {
862 0     0 0 0 my $self = shift;
863 0         0 my %args = @_;
864              
865 0 0 0     0 if ( exists $args{ctype}
866             and exists $self->{typemap_lookup}{tidy_type($args{ctype})} )
867             {
868 0         0 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
869             }
870              
871 0 0 0     0 if ( exists $args{inputmap_xstype}
872             and exists $self->{input_lookup}{$args{inputmap_xstype}} )
873             {
874 0         0 die("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
875             }
876              
877 0 0 0     0 if ( exists $args{outputmap_xstype}
878             and exists $self->{output_lookup}{$args{outputmap_xstype}} )
879             {
880 0         0 die("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
881             }
882              
883 0         0 return 1;
884             }
885              
886             =head2 clone
887              
888             Creates and returns a clone of a full typemaps object.
889              
890             Takes named parameters: If C is true,
891             the clone will share the actual individual type/input/outputmap objects,
892             but not share their storage. Use with caution. Without C,
893             the clone will be fully independent.
894              
895             =cut
896              
897             sub clone {
898 0     0 1 0 my $proto = shift;
899 0         0 my %args = @_;
900              
901 0         0 my $self;
902 0 0       0 if ($args{shallow}) {
903             $self = bless( {
904             %$proto,
905 0         0 typemap_section => [@{$proto->{typemap_section}}],
906 0         0 typemap_lookup => {%{$proto->{typemap_lookup}}},
907 0         0 input_section => [@{$proto->{input_section}}],
908 0         0 input_lookup => {%{$proto->{input_lookup}}},
909 0         0 output_section => [@{$proto->{output_section}}],
910 0         0 output_lookup => {%{$proto->{output_lookup}}},
  0         0  
911             } => ref($proto) );
912             }
913             else {
914             $self = bless( {
915             %$proto,
916 0         0 typemap_section => [map $_->new, @{$proto->{typemap_section}}],
917 0         0 typemap_lookup => {%{$proto->{typemap_lookup}}},
918 0         0 input_section => [map $_->new, @{$proto->{input_section}}],
919 0         0 input_lookup => {%{$proto->{input_lookup}}},
920 0         0 output_section => [map $_->new, @{$proto->{output_section}}],
921 0         0 output_lookup => {%{$proto->{output_lookup}}},
  0         0  
922             } => ref($proto) );
923             }
924              
925 0         0 return $self;
926             }
927              
928             =head2 tidy_type
929              
930             Function to (heuristically) canonicalize a C type in terms of white space.
931             Works to some degree with C++ types. For example,
932              
933             $halfway_canonical_type = tidy_type(' int * * const * ');
934              
935             returns C<'int ** const *'>.
936              
937             Moved from C.
938              
939             =cut
940              
941             sub tidy_type {
942 361     361 1 867 local $_ = shift;
943              
944             # for templated C++ types, do some bit of flawed canonicalization
945             # wrt. templates at least
946 361 50       1171 if (/[<>]/) {
947 0         0 s/\s*([<>])\s*/$1/g;
948 0         0 s/>>/> >/g;
949             }
950              
951             # rationalise any '*' by joining them into bunches and removing whitespace
952 361         1362 s#\s*(\*+)\s*#$1#g;
953 361         1289 s#(\*+)# $1 #g ;
954              
955             # trim leading & trailing whitespace
956 361         902 s/^\s+//; s/\s+$//;
  361         949  
957              
958             # change multiple whitespace into a single space
959 361         928 s/\s+/ /g;
960              
961 361         1265 $_;
962             }
963              
964              
965              
966             sub _parse {
967             my $self = shift;
968             my $stringref = shift;
969             my $lineno_offset = shift;
970             $lineno_offset = 0 if not defined $lineno_offset;
971             my $filename = shift;
972             $filename = '' if not defined $filename;
973              
974             my $replace = $self->{replace};
975             my $skip = $self->{skip};
976             die "Can only replace OR skip" if $replace and $skip;
977             my @add_params;
978             push @add_params, replace => 1 if $replace;
979             push @add_params, skip => 1 if $skip;
980              
981             # TODO comments should round-trip, currently ignoring
982             # TODO order of sections, multiple sections of same type
983             # Heavily influenced by XS::Install::FrozenShit::ParseXS
984             my $section = 'typemap';
985             my $lineno = $lineno_offset;
986             my $junk = "";
987             my $current = \$junk;
988             my @input_expr;
989             my @output_expr;
990             while ($$stringref =~ /^(.*)$/gcm) {
991             local $_ = $1;
992             ++$lineno;
993             chomp;
994             next if /^\s*#/;
995             if (/^INPUT\s*$/) {
996             $section = 'input';
997             $current = \$junk;
998             next;
999             }
1000             elsif (/^OUTPUT\s*$/) {
1001             $section = 'output';
1002             $current = \$junk;
1003             next;
1004             }
1005             elsif (/^TYPEMAP\s*$/) {
1006             $section = 'typemap';
1007             $current = \$junk;
1008             next;
1009             }
1010            
1011             if ($section eq 'typemap') {
1012             my $line = $_;
1013             s/^\s+//; s/\s+$//;
1014             next if $_ eq '' or /^#/;
1015             my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($XS::Install::FrozenShit::ParseXS::Constants::PrototypeRegexp*)$/o
1016             or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
1017             next;
1018              
1019             if (length($proto)) {
1020             warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
1021             unless _valid_proto_string($proto);
1022             }
1023             $self->add_typemap(
1024             XS::Install::FrozenShit::Typemaps::Type->new(
1025             xstype => $kind,
1026             (length($proto) ? (prototype => $proto) : ()),
1027             ctype => $type
1028             ),
1029             @add_params
1030             );
1031             } elsif (/^\s/) {
1032             s/\s+$//;
1033             $$current .= $$current eq '' ? $_ : "\n".$_;
1034             } elsif ($_ eq '') {
1035             next;
1036             } elsif ($section eq 'input') {
1037             s/\s+$//;
1038             push @input_expr, {xstype => $_, code => ''};
1039             $current = \$input_expr[-1]{code};
1040             } else { # output section
1041             s/\s+$//;
1042             push @output_expr, {xstype => $_, code => ''};
1043             $current = \$output_expr[-1]{code};
1044             }
1045              
1046             } # end while lines
1047              
1048             foreach my $inexpr (@input_expr) {
1049             $self->add_inputmap( XS::Install::FrozenShit::Typemaps::InputMap->new(%$inexpr), @add_params );
1050             }
1051             foreach my $outexpr (@output_expr) {
1052             $self->add_outputmap( XS::Install::FrozenShit::Typemaps::OutputMap->new(%$outexpr), @add_params );
1053             }
1054              
1055             return 1;
1056             }
1057              
1058             # taken from XS::Install::FrozenShit::ParseXS
1059             sub _valid_proto_string {
1060 0     0     my $string = shift;
1061 0 0         if ($string =~ /^$XS::Install::FrozenShit::ParseXS::Constants::PrototypeRegexp+$/o) {
1062 0           return $string;
1063             }
1064              
1065 0           return 0 ;
1066             }
1067              
1068             # taken from XS::Install::FrozenShit::ParseXS (C_string)
1069             sub _escape_backslashes {
1070 0     0     my $string = shift;
1071 0           $string =~ s[\\][\\\\]g;
1072 0           $string;
1073             }
1074              
1075             =head1 CAVEATS
1076              
1077             Inherits some evil code from C.
1078              
1079             =head1 SEE ALSO
1080              
1081             The parser is heavily inspired from the one in L.
1082              
1083             For details on typemaps: L, L.
1084              
1085             =head1 AUTHOR
1086              
1087             Steffen Mueller C<>
1088              
1089             =head1 COPYRIGHT & LICENSE
1090              
1091             Copyright 2009, 2010, 2011, 2012, 2013 Steffen Mueller
1092              
1093             This program is free software; you can redistribute it and/or
1094             modify it under the same terms as Perl itself.
1095              
1096             =cut
1097              
1098             1;
1099