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 it if necessary.
517 Mandatory named arguments: Either C<typemap =E<gt> $another_typemap_obj>
518 or C<file =E<gt> $path_to_typemap_file> but not both.
520 Optional argument: C<replace =E<gt> 1> to force replacement
521 of existing typemap entries without warning.
529 if (exists $args{typemap} and exists $args{file}) {
530 croak("Need {file} OR {typemap} argument. Not both!");
532 elsif (not exists $args{typemap} and not exists $args{file}) {
533 croak("Need {file} or {typemap} argument!");
536 my $typemap = $args{typemap};
537 if (not defined $typemap) {
538 $typemap = ref($self)->new(file => $args{file});
541 my $replace = $args{replace};
543 # FIXME breaking encapsulation. Add accessor code.
545 foreach my $entry (@{$typemap->{typemap_section}}) {
546 $self->add_typemap( $entry );
549 foreach my $entry (@{$typemap->{input_section}}) {
550 $self->add_inputmap( $entry );
553 foreach my $entry (@{$typemap->{output_section}}) {
554 $self->add_outputmap( $entry );
561 =head2 _get_typemap_hash
563 Returns a hash mapping the C types to the XS types:
566 'char **' => 'T_PACKEDARRAY',
569 'InputStream' => 'T_IN',
570 'double' => 'T_DOUBLE',
574 This is documented because it is used by C<ExtUtils::ParseXS>,
575 but it's not intended for general consumption. May be removed
580 sub _get_typemap_hash {
582 my $lookup = $self->{typemap_lookup};
583 my $storage = $self->{typemap_section};
586 foreach my $ctype (keys %$lookup) {
587 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
593 =head2 _get_inputmap_hash
595 Returns a hash mapping the XS types (identifiers) to the
596 corresponding INPUT code:
599 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
601 'T_OUT' => ' $var = IoOFP(sv_2io($arg))
603 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
607 This is documented because it is used by C<ExtUtils::ParseXS>,
608 but it's not intended for general consumption. May be removed
613 sub _get_inputmap_hash {
615 my $lookup = $self->{input_lookup};
616 my $storage = $self->{input_section};
619 foreach my $xstype (keys %$lookup) {
620 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
627 =head2 _get_outputmap_hash
629 Returns a hash mapping the XS types (identifiers) to the
630 corresponding OUTPUT code:
633 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
634 $var.context.value().size());
637 GV *gv = newGVgen("$Package");
638 if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
639 sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1)));
647 This is documented because it is used by C<ExtUtils::ParseXS>,
648 but it's not intended for general consumption. May be removed
653 sub _get_outputmap_hash {
655 my $lookup = $self->{output_lookup};
656 my $storage = $self->{output_section};
659 foreach my $xstype (keys %$lookup) {
660 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
666 =head2 _get_prototype_hash
668 Returns a hash mapping the C types of the typemap to their
669 corresponding prototypes.
675 'InputStream' => '$',
680 This is documented because it is used by C<ExtUtils::ParseXS>,
681 but it's not intended for general consumption. May be removed
686 sub _get_prototype_hash {
688 my $lookup = $self->{typemap_lookup};
689 my $storage = $self->{typemap_section};
692 foreach my $ctype (keys %$lookup) {
693 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
701 # make sure that the provided types wouldn't collide with what's
702 # in the object already.
707 if ( exists $args{ctype}
708 and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} )
710 croak("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
713 if ( exists $args{inputmap_xstype}
714 and exists $self->{input_lookup}{$args{inputmap_xstype}} )
716 croak("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
719 if ( exists $args{outputmap_xstype}
720 and exists $self->{output_lookup}{$args{outputmap_xstype}} )
722 croak("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
730 my $stringref = shift;
731 my $filename = shift;
732 $filename = '<string>' if not defined $filename;
734 # TODO comments should round-trip, currently ignoring
735 # TODO order of sections, multiple sections of same type
736 # Heavily influenced by ExtUtils::ParseXS
737 my $section = 'typemap';
740 my $current = \$junk;
743 while ($$stringref =~ /^(.*)$/gcm) {
753 elsif (/^OUTPUT\s*$/) {
758 elsif (/^TYPEMAP\s*$/) {
759 $section = 'typemap';
764 if ($section eq 'typemap') {
767 next if /^#/ or /^$/;
768 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
769 or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
771 #$proto = '' if not $proto;
772 # prototype defaults to '$'
773 #$proto = '$' unless $proto;
774 #warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
775 # unless _valid_proto_string($proto);
777 ExtUtils::Typemaps::Type->new(
778 xstype => $kind, proto => $proto, ctype => $type
782 $$current .= $$current eq '' ? $_ : "\n".$_;
785 } elsif ($section eq 'input') {
787 push @input_expr, {xstype => $_, code => ''};
788 $current = \$input_expr[-1]{code};
789 } else { # output section
791 push @output_expr, {xstype => $_, code => ''};
792 $current = \$output_expr[-1]{code};
797 foreach my $inexpr (@input_expr) {
798 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr) );
800 foreach my $outexpr (@output_expr) {
801 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr) );
807 # taken from ExtUtils::ParseXS
811 # rationalise any '*' by joining them into bunches and removing whitespace
815 # trim leading & trailing whitespace
818 # change multiple whitespace into a single space
825 # taken from ExtUtils::ParseXS
826 sub _valid_proto_string {
828 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
835 # taken from ExtUtils::ParseXS (C_string)
836 sub _escape_backslashes {
838 $string =~ s[\\][\\\\]g;
844 Inherits some evil code from C<ExtUtils::ParseXS>.
848 The parser is heavily inspired from the one in L<ExtUtils::ParseXS>.
850 For details on typemaps: L<perlxstut>, L<perlxs>.
854 Steffen Mueller C<<smueller@cpan.org>>
856 =head1 COPYRIGHT & LICENSE
858 Copyright 2009-2011 Steffen Mueller
860 This program is free software; you can redistribute it and/or
861 modify it under the same terms as Perl itself.