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