1 package ExtUtils::Typemaps;
8 require ExtUtils::ParseXS;
9 require ExtUtils::ParseXS::Constants;
10 require ExtUtils::Typemaps::InputMap;
11 require ExtUtils::Typemaps::OutputMap;
12 require ExtUtils::Typemaps::Type;
16 ExtUtils::Typemaps - Read/Write/Modify Perl/XS typemap files
21 my $typemap = ExtUtils::Typemaps->new(file => 'typemap');
22 # alternatively create an in-memory typemap
23 # $typemap = ExtUtils::Typemaps->new();
24 # alternatively create an in-memory typemap by parsing a string
25 # $typemap = ExtUtils::Typemaps->new(string => $sometypemap);
28 $typemap->add_typemap(ctype => 'NV', xstype => 'T_NV');
29 $typemap->add_inputmap (xstype => 'T_NV', code => '$var = ($type)SvNV($arg);');
30 $typemap->add_outputmap(xstype => 'T_NV', code => 'sv_setnv($arg, (NV)$var);');
31 $typemap->add_string(string => $typemapstring); # will be parsed and merged
33 # remove a mapping (same for remove_typemap and remove_outputmap...)
34 $typemap->remove_inputmap(xstype => 'SomeType');
36 # save a typemap to a file
37 $typemap->write(file => 'anotherfile.map');
39 # merge the other typemap into this one
40 $typemap->merge(typemap => $another_typemap);
44 This module can read, modify, create and write Perl XS typemap files. If you don't know
45 what a typemap is, please confer the L<perlxstut> and L<perlxs> manuals.
47 The module is not entirely round-trip safe: For example it currently simply strips all comments.
48 The order of entries in the maps is, however, preserved.
50 We check for duplicate entries in the typemap, but do not check for missing
51 C<TYPEMAP> entries for C<INPUTMAP> or C<OUTPUTMAP> entries since these might be hidden
52 in a different typemap.
60 Returns a new typemap object. Takes an optional C<file> parameter.
61 If set, the given file will be read. If the file doesn't exist, an empty typemap
64 Alternatively, if the C<string> parameter is given, the supplied
65 string will be parsed instead of a file.
73 if (defined $args{file} and defined $args{string}) {
74 croak("Cannot handle both 'file' and 'string' arguments to constructor");
80 typemap_section => [],
95 if (defined $self->{string}) {
96 $self->_parse(\($self->{string}));
97 delete $self->{string};
99 elsif (defined $self->{file} and -e $self->{file}) {
100 open my $fh, '<', $self->{file}
101 or die "Cannot open typemap file '"
102 . $self->{file} . "' for reading: $!";
105 $self->_parse(\$string, $self->{file});
111 Get/set the file that the typemap is written to when the
112 C<write> method is called.
117 $_[0]->{file} = $_[1] if @_ > 1;
123 Add a C<TYPEMAP> entry to the typemap.
125 Required named arguments: The C<ctype> (e.g. C<ctype =E<gt> 'double'>)
126 and the C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>).
128 Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of
129 existing C<TYPEMAP> entries of the same C<ctype>.
131 As an alternative to the named parameters usage, you may pass in
132 an C<ExtUtils::Typemaps::Type> object, a copy of which will be
133 added to the typemap.
143 $type = $orig->new(@_);
147 my $ctype = $args{ctype};
148 croak("Need ctype argument") if not defined $ctype;
149 my $xstype = $args{xstype};
150 croak("Need xstype argument") if not defined $xstype;
152 $type = ExtUtils::Typemaps::Type->new(
154 'prototype' => $args{'prototype'},
157 $replace = $args{replace};
161 $self->remove_typemap(ctype => $type->ctype);
163 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
167 push @{$self->{typemap_section}}, $type;
168 # remember type for lookup, too.
169 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
176 Add an C<INPUT> entry to the typemap.
178 Required named arguments:
179 The C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>)
180 and the C<code> to associate with it for input.
182 Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of
183 existing C<INPUT> entries of the same C<xstype>.
185 You may pass in a single C<ExtUtils::Typemaps::InputMap> object instead,
186 a copy of which will be added to the typemap.
196 $input = $orig->new(@_);
200 my $xstype = $args{xstype};
201 croak("Need xstype argument") if not defined $xstype;
202 my $code = $args{code};
203 croak("Need code argument") if not defined $code;
205 $input = ExtUtils::Typemaps::InputMap->new(
209 $replace = $args{replace};
212 $self->remove_inputmap(xstype => $input->xstype);
214 $self->validate(inputmap_xstype => $input->xstype);
218 push @{$self->{input_section}}, $input;
219 # remember type for lookup, too.
220 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
227 Add an C<OUTPUT> entry to the typemap.
228 Works exactly the same as C<add_inputmap>.
238 $output = $orig->new(@_);
242 my $xstype = $args{xstype};
243 croak("Need xstype argument") if not defined $xstype;
244 my $code = $args{code};
245 croak("Need code argument") if not defined $code;
247 $output = ExtUtils::Typemaps::OutputMap->new(
251 $replace = $args{replace};
254 $self->remove_outputmap(xstype => $output->xstype);
256 $self->validate(outputmap_xstype => $output->xstype);
260 push @{$self->{output_section}}, $output;
261 # remember type for lookup, too.
262 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
269 Parses a string as a typemap and merge it into the typemap object.
271 Required named argument: C<string> to specify the string to parse.
278 croak("Need 'string' argument") if not defined $args{string};
280 # no, this is not elegant.
281 my $other = ExtUtils::Typemaps->new(string => $args{string});
282 $self->merge(typemap => $other);
285 =head2 remove_typemap
287 Removes a C<TYPEMAP> entry from the typemap.
289 Required named argument: C<ctype> to specify the entry to remove from the typemap.
291 Alternatively, you may pass a single C<ExtUtils::Typemaps::Type> object.
300 $ctype = $args{ctype};
301 croak("Need ctype argument") if not defined $ctype;
302 $ctype = _tidy_type($ctype);
305 $ctype = $_[0]->tidy_ctype;
308 return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup});
311 =head2 remove_inputmap
313 Removes an C<INPUT> entry from the typemap.
315 Required named argument: C<xstype> to specify the entry to remove from the typemap.
317 Alternatively, you may pass a single C<ExtUtils::Typemaps::InputMap> object.
321 sub remove_inputmap {
326 $xstype = $args{xstype};
327 croak("Need xstype argument") if not defined $xstype;
330 $xstype = $_[0]->xstype;
333 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup});
336 =head2 remove_inputmap
338 Removes an C<OUTPUT> entry from the typemap.
340 Required named argument: C<xstype> to specify the entry to remove from the typemap.
342 Alternatively, you may pass a single C<ExtUtils::Typemaps::OutputMap> object.
346 sub remove_outputmap {
351 $xstype = $args{xstype};
352 croak("Need xstype argument") if not defined $xstype;
355 $xstype = $_[0]->xstype;
358 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
367 # Just fetch the index of the item from the lookup table
368 my $index = $lookup->{$rm};
369 return() if not defined $index;
371 # Nuke the item from storage
372 splice(@$array, $index, 1);
374 # Decrement the storage position of all items thereafter
375 foreach my $key (keys %$lookup) {
376 if ($lookup->{$key} > $index) {
385 Fetches an entry of the TYPEMAP section of the typemap.
387 Mandatory named arguments: The C<ctype> of the entry.
389 Returns the C<ExtUtils::Typemaps::Type>
390 object for the entry if found.
397 my $ctype = $args{ctype};
398 croak("Need ctype argument") if not defined $ctype;
399 $ctype = _tidy_type($ctype);
401 my $index = $self->{typemap_lookup}{$ctype};
402 return() if not defined $index;
403 return $self->{typemap_section}[$index];
408 Fetches an entry of the INPUT section of the
411 Mandatory named arguments: The C<xstype> of the
414 Returns the C<ExtUtils::Typemaps::InputMap>
415 object for the entry if found.
422 my $xstype = $args{xstype};
423 croak("Need xstype argument") if not defined $xstype;
425 my $index = $self->{input_lookup}{$xstype};
426 return() if not defined $index;
427 return $self->{input_section}[$index];
432 Fetches an entry of the OUTPUT section of the
435 Mandatory named arguments: The C<xstype> of the
438 Returns the C<ExtUtils::Typemaps::InputMap>
439 object for the entry if found.
446 my $xstype = $args{xstype};
447 croak("Need xstype argument") if not defined $xstype;
449 my $index = $self->{output_lookup}{$xstype};
450 return() if not defined $index;
451 return $self->{output_section}[$index];
456 Write the typemap to a file. Optionally takes a C<file> argument. If given, the
457 typemap will be written to the specified file. If not, the typemap is written
458 to the currently stored file name (see C<-E<gt>file> above, this defaults to the file
459 it was read from if any).
466 my $file = defined $args{file} ? $args{file} : $self->file();
467 croak("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
468 if not defined $file;
470 open my $fh, '>', $file
471 or die "Cannot open typemap file '$file' for writing: $!";
472 print $fh $self->as_string();
478 Generates and returns the string form of the typemap.
484 my $typemap = $self->{typemap_section};
486 push @code, "TYPEMAP\n";
487 foreach my $entry (@$typemap) {
489 # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
490 push @code, $entry->ctype . "\t" . $entry->xstype
491 . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
494 my $input = $self->{input_section};
496 push @code, "\nINPUT\n";
497 foreach my $entry (@$input) {
498 push @code, $entry->xstype, "\n", $entry->code, "\n";
502 my $output = $self->{output_section};
504 push @code, "\nOUTPUT\n";
505 foreach my $entry (@$output) {
506 push @code, $entry->xstype, "\n", $entry->code, "\n";
509 return join '', @code;
514 Merges a given typemap into the object. Note that a failed merge
515 operation leaves the object in an inconsistent state so clone if necessary.
517 Mandatory named argument: C<typemap =E<gt> $another_typemap>
519 Optional argument: C<replace =E<gt> 1> to force replacement
520 of existing typemap entries without warning.
527 my $typemap = $args{typemap};
528 croak("Need ExtUtils::Typemaps as argument")
529 if not ref $typemap or not $typemap->isa('ExtUtils::Typemaps');
531 my $replace = $args{replace};
533 # FIXME breaking encapsulation. Add accessor code.
535 foreach my $entry (@{$typemap->{typemap_section}}) {
536 $self->add_typemap( $entry );
539 foreach my $entry (@{$typemap->{input_section}}) {
540 $self->add_inputmap( $entry );
543 foreach my $entry (@{$typemap->{output_section}}) {
544 $self->add_outputmap( $entry );
551 =head2 _get_typemap_hash
553 Returns a hash mapping the C types to the XS types:
556 'char **' => 'T_PACKEDARRAY',
559 'InputStream' => 'T_IN',
560 'double' => 'T_DOUBLE',
564 This is documented because it is used by C<ExtUtils::ParseXS>,
565 but it's not intended for general consumption. May be removed
570 sub _get_typemap_hash {
572 my $lookup = $self->{typemap_lookup};
573 my $storage = $self->{typemap_section};
576 foreach my $ctype (keys %$lookup) {
577 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
583 =head2 _get_inputmap_hash
585 Returns a hash mapping the XS types (identifiers) to the
586 corresponding INPUT code:
589 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
591 'T_OUT' => ' $var = IoOFP(sv_2io($arg))
593 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
597 This is documented because it is used by C<ExtUtils::ParseXS>,
598 but it's not intended for general consumption. May be removed
603 sub _get_inputmap_hash {
605 my $lookup = $self->{input_lookup};
606 my $storage = $self->{input_section};
609 foreach my $xstype (keys %$lookup) {
610 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
617 =head2 _get_outputmap_hash
619 Returns a hash mapping the XS types (identifiers) to the
620 corresponding OUTPUT code:
623 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
624 $var.context.value().size());
627 GV *gv = newGVgen("$Package");
628 if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
629 sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1)));
637 This is documented because it is used by C<ExtUtils::ParseXS>,
638 but it's not intended for general consumption. May be removed
643 sub _get_outputmap_hash {
645 my $lookup = $self->{output_lookup};
646 my $storage = $self->{output_section};
649 foreach my $xstype (keys %$lookup) {
650 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
659 # make sure that the provided types wouldn't collide with what's
660 # in the object already.
665 if ( exists $args{ctype}
666 and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} )
668 croak("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
671 if ( exists $args{inputmap_xstype}
672 and exists $self->{input_lookup}{$args{inputmap_xstype}} )
674 croak("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
677 if ( exists $args{outputmap_xstype}
678 and exists $self->{output_lookup}{$args{outputmap_xstype}} )
680 croak("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
688 my $stringref = shift;
689 my $filename = shift;
690 $filename = '<string>' if not defined $filename;
692 # TODO comments should round-trip, currently ignoring
693 # TODO order of sections, multiple sections of same type
694 # Heavily influenced by ExtUtils::ParseXS
695 my $section = 'typemap';
698 my $current = \$junk;
701 while ($$stringref =~ /^(.*)$/gcm) {
711 elsif (/^OUTPUT\s*$/) {
716 elsif (/^TYPEMAP\s*$/) {
717 $section = 'typemap';
722 if ($section eq 'typemap') {
725 next if /^#/ or /^$/;
726 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
727 or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
729 #$proto = '' if not $proto;
730 # prototype defaults to '$'
731 #$proto = '$' unless $proto;
732 #warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
733 # unless _valid_proto_string($proto);
735 ExtUtils::Typemaps::Type->new(
736 xstype => $kind, proto => $proto, ctype => $type
740 $$current .= $$current eq '' ? $_ : "\n".$_;
743 } elsif ($section eq 'input') {
745 push @input_expr, {xstype => $_, code => ''};
746 $current = \$input_expr[-1]{code};
747 } else { # output section
749 push @output_expr, {xstype => $_, code => ''};
750 $current = \$output_expr[-1]{code};
755 foreach my $inexpr (@input_expr) {
756 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr) );
758 foreach my $outexpr (@output_expr) {
759 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr) );
765 # taken from ExtUtils::ParseXS
769 # rationalise any '*' by joining them into bunches and removing whitespace
773 # trim leading & trailing whitespace
776 # change multiple whitespace into a single space
783 # taken from ExtUtils::ParseXS
784 sub _valid_proto_string {
786 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
793 # taken from ExtUtils::ParseXS (C_string)
794 sub _escape_backslashes {
796 $string =~ s[\\][\\\\]g;
802 Inherits some evil code from C<ExtUtils::ParseXS>.
806 The parser is heavily inspired from the one in L<ExtUtils::ParseXS>.
808 For details on typemaps: L<perlxstut>, L<perlxs>.
812 Steffen Mueller C<<smueller@cpan.org>>
814 =head1 COPYRIGHT & LICENSE
816 Copyright 2009-2011 Steffen Mueller
818 This program is free software; you can redistribute it and/or
819 modify it under the same terms as Perl itself.