Commit | Line | Data |
---|---|---|
7320491e | 1 | package ExtUtils::Typemaps; |
297f4492 S |
2 | use 5.006001; |
3 | use strict; | |
4 | use warnings; | |
120f2c87 | 5 | our $VERSION = '1.00'; |
297f4492 S |
6 | use Carp qw(croak); |
7 | ||
9c95e74b S |
8 | require ExtUtils::ParseXS; |
9 | require ExtUtils::ParseXS::Constants; | |
7320491e S |
10 | require ExtUtils::Typemaps::InputMap; |
11 | require ExtUtils::Typemaps::OutputMap; | |
12 | require ExtUtils::Typemaps::Type; | |
297f4492 S |
13 | |
14 | =head1 NAME | |
15 | ||
7320491e | 16 | ExtUtils::Typemaps - Read/Write/Modify Perl/XS typemap files |
297f4492 S |
17 | |
18 | =head1 SYNOPSIS | |
19 | ||
20 | # read/create file | |
7320491e | 21 | my $typemap = ExtUtils::Typemaps->new(file => 'typemap'); |
297f4492 | 22 | # alternatively create an in-memory typemap |
7320491e | 23 | # $typemap = ExtUtils::Typemaps->new(); |
297f4492 | 24 | # alternatively create an in-memory typemap by parsing a string |
7320491e | 25 | # $typemap = ExtUtils::Typemaps->new(string => $sometypemap); |
297f4492 S |
26 | |
27 | # add a mapping | |
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 | |
32 | ||
33 | # remove a mapping (same for remove_typemap and remove_outputmap...) | |
34 | $typemap->remove_inputmap(xstype => 'SomeType'); | |
35 | ||
36 | # save a typemap to a file | |
37 | $typemap->write(file => 'anotherfile.map'); | |
38 | ||
39 | # merge the other typemap into this one | |
40 | $typemap->merge(typemap => $another_typemap); | |
41 | ||
42 | =head1 DESCRIPTION | |
43 | ||
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. | |
46 | ||
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. | |
49 | ||
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. | |
53 | ||
54 | =head1 METHODS | |
55 | ||
56 | =cut | |
57 | ||
58 | =head2 new | |
59 | ||
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 | |
62 | is returned. | |
63 | ||
64 | Alternatively, if the C<string> parameter is given, the supplied | |
65 | string will be parsed instead of a file. | |
66 | ||
67 | =cut | |
68 | ||
69 | sub new { | |
70 | my $class = shift; | |
71 | my %args = @_; | |
72 | ||
73 | if (defined $args{file} and defined $args{string}) { | |
74 | croak("Cannot handle both 'file' and 'string' arguments to constructor"); | |
75 | } | |
76 | ||
77 | my $self = bless { | |
78 | file => undef, | |
79 | %args, | |
80 | typemap_section => [], | |
256eb880 | 81 | typemap_lookup => {}, |
297f4492 | 82 | input_section => [], |
7534260c | 83 | input_lookup => {}, |
297f4492 | 84 | output_section => [], |
080872fe | 85 | output_lookup => {}, |
297f4492 S |
86 | } => $class; |
87 | ||
88 | $self->_init(); | |
89 | ||
90 | return $self; | |
91 | } | |
92 | ||
93 | sub _init { | |
94 | my $self = shift; | |
95 | if (defined $self->{string}) { | |
96 | $self->_parse(\($self->{string})); | |
97 | delete $self->{string}; | |
98 | } | |
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: $!"; | |
103 | local $/ = undef; | |
104 | my $string = <$fh>; | |
105 | $self->_parse(\$string, $self->{file}); | |
106 | } | |
107 | } | |
108 | ||
109 | =head2 file | |
110 | ||
111 | Get/set the file that the typemap is written to when the | |
112 | C<write> method is called. | |
113 | ||
114 | =cut | |
115 | ||
116 | sub file { | |
117 | $_[0]->{file} = $_[1] if @_ > 1; | |
118 | $_[0]->{file} | |
119 | } | |
120 | ||
121 | =head2 add_typemap | |
122 | ||
123 | Add a C<TYPEMAP> entry to the typemap. | |
124 | ||
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'>). | |
127 | ||
128 | Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of | |
129 | existing C<TYPEMAP> entries of the same C<ctype>. | |
130 | ||
131 | As an alternative to the named parameters usage, you may pass in | |
7320491e | 132 | an C<ExtUtils::Typemaps::Type> object, a copy of which will be |
297f4492 S |
133 | added to the typemap. |
134 | ||
135 | =cut | |
136 | ||
137 | sub add_typemap { | |
138 | my $self = shift; | |
139 | my $type; | |
140 | my $replace = 0; | |
141 | if (@_ == 1) { | |
142 | my $orig = shift; | |
143 | $type = $orig->new(@_); | |
144 | } | |
145 | else { | |
146 | my %args = @_; | |
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; | |
151 | ||
7320491e | 152 | $type = ExtUtils::Typemaps::Type->new( |
297f4492 S |
153 | xstype => $xstype, |
154 | 'prototype' => $args{'prototype'}, | |
155 | ctype => $ctype, | |
156 | ); | |
157 | $replace = $args{replace}; | |
158 | } | |
159 | ||
160 | if ($replace) { | |
161 | $self->remove_typemap(ctype => $type->ctype); | |
162 | } else { | |
163 | $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype); | |
164 | } | |
256eb880 S |
165 | |
166 | # store | |
297f4492 | 167 | push @{$self->{typemap_section}}, $type; |
256eb880 S |
168 | # remember type for lookup, too. |
169 | $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}}; | |
7534260c | 170 | |
297f4492 S |
171 | return 1; |
172 | } | |
173 | ||
174 | =head2 add_inputmap | |
175 | ||
176 | Add an C<INPUT> entry to the typemap. | |
177 | ||
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. | |
181 | ||
182 | Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of | |
183 | existing C<INPUT> entries of the same C<xstype>. | |
184 | ||
7320491e | 185 | You may pass in a single C<ExtUtils::Typemaps::InputMap> object instead, |
297f4492 S |
186 | a copy of which will be added to the typemap. |
187 | ||
188 | =cut | |
189 | ||
190 | sub add_inputmap { | |
191 | my $self = shift; | |
192 | my $input; | |
193 | my $replace = 0; | |
194 | if (@_ == 1) { | |
195 | my $orig = shift; | |
196 | $input = $orig->new(@_); | |
197 | } | |
198 | else { | |
199 | my %args = @_; | |
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; | |
204 | ||
7320491e | 205 | $input = ExtUtils::Typemaps::InputMap->new( |
297f4492 S |
206 | xstype => $xstype, |
207 | code => $code, | |
208 | ); | |
209 | $replace = $args{replace}; | |
210 | } | |
211 | if ($replace) { | |
212 | $self->remove_inputmap(xstype => $input->xstype); | |
213 | } else { | |
214 | $self->validate(inputmap_xstype => $input->xstype); | |
215 | } | |
7534260c S |
216 | |
217 | # store | |
297f4492 | 218 | push @{$self->{input_section}}, $input; |
7534260c S |
219 | # remember type for lookup, too. |
220 | $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}}; | |
221 | ||
297f4492 S |
222 | return 1; |
223 | } | |
224 | ||
225 | =head2 add_outputmap | |
226 | ||
227 | Add an C<OUTPUT> entry to the typemap. | |
228 | Works exactly the same as C<add_inputmap>. | |
229 | ||
230 | =cut | |
231 | ||
232 | sub add_outputmap { | |
233 | my $self = shift; | |
234 | my $output; | |
235 | my $replace = 0; | |
236 | if (@_ == 1) { | |
237 | my $orig = shift; | |
238 | $output = $orig->new(@_); | |
239 | } | |
240 | else { | |
241 | my %args = @_; | |
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; | |
246 | ||
7320491e | 247 | $output = ExtUtils::Typemaps::OutputMap->new( |
297f4492 S |
248 | xstype => $xstype, |
249 | code => $code, | |
250 | ); | |
251 | $replace = $args{replace}; | |
252 | } | |
253 | if ($replace) { | |
254 | $self->remove_outputmap(xstype => $output->xstype); | |
255 | } else { | |
256 | $self->validate(outputmap_xstype => $output->xstype); | |
257 | } | |
080872fe S |
258 | |
259 | # store | |
297f4492 | 260 | push @{$self->{output_section}}, $output; |
080872fe S |
261 | # remember type for lookup, too. |
262 | $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}}; | |
263 | ||
297f4492 S |
264 | return 1; |
265 | } | |
266 | ||
267 | =head2 add_string | |
268 | ||
269 | Parses a string as a typemap and merge it into the typemap object. | |
270 | ||
271 | Required named argument: C<string> to specify the string to parse. | |
272 | ||
273 | =cut | |
274 | ||
275 | sub add_string { | |
276 | my $self = shift; | |
277 | my %args = @_; | |
278 | croak("Need 'string' argument") if not defined $args{string}; | |
279 | ||
280 | # no, this is not elegant. | |
7320491e | 281 | my $other = ExtUtils::Typemaps->new(string => $args{string}); |
297f4492 S |
282 | $self->merge(typemap => $other); |
283 | } | |
284 | ||
285 | =head2 remove_typemap | |
286 | ||
287 | Removes a C<TYPEMAP> entry from the typemap. | |
288 | ||
289 | Required named argument: C<ctype> to specify the entry to remove from the typemap. | |
290 | ||
7320491e | 291 | Alternatively, you may pass a single C<ExtUtils::Typemaps::Type> object. |
297f4492 S |
292 | |
293 | =cut | |
294 | ||
295 | sub remove_typemap { | |
296 | my $self = shift; | |
297 | my $ctype; | |
298 | if (@_ > 1) { | |
299 | my %args = @_; | |
300 | $ctype = $args{ctype}; | |
301 | croak("Need ctype argument") if not defined $ctype; | |
302 | $ctype = _tidy_type($ctype); | |
303 | } | |
304 | else { | |
305 | $ctype = $_[0]->tidy_ctype; | |
306 | } | |
256eb880 | 307 | |
4433194b | 308 | return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup}); |
297f4492 S |
309 | } |
310 | ||
311 | =head2 remove_inputmap | |
312 | ||
313 | Removes an C<INPUT> entry from the typemap. | |
314 | ||
315 | Required named argument: C<xstype> to specify the entry to remove from the typemap. | |
316 | ||
7320491e | 317 | Alternatively, you may pass a single C<ExtUtils::Typemaps::InputMap> object. |
297f4492 S |
318 | |
319 | =cut | |
320 | ||
321 | sub remove_inputmap { | |
322 | my $self = shift; | |
323 | my $xstype; | |
324 | if (@_ > 1) { | |
325 | my %args = @_; | |
326 | $xstype = $args{xstype}; | |
327 | croak("Need xstype argument") if not defined $xstype; | |
328 | } | |
329 | else { | |
330 | $xstype = $_[0]->xstype; | |
331 | } | |
332 | ||
4433194b | 333 | return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup}); |
297f4492 S |
334 | } |
335 | ||
336 | =head2 remove_inputmap | |
337 | ||
338 | Removes an C<OUTPUT> entry from the typemap. | |
339 | ||
340 | Required named argument: C<xstype> to specify the entry to remove from the typemap. | |
341 | ||
7320491e | 342 | Alternatively, you may pass a single C<ExtUtils::Typemaps::OutputMap> object. |
297f4492 S |
343 | |
344 | =cut | |
345 | ||
346 | sub remove_outputmap { | |
347 | my $self = shift; | |
348 | my $xstype; | |
349 | if (@_ > 1) { | |
350 | my %args = @_; | |
351 | $xstype = $args{xstype}; | |
352 | croak("Need xstype argument") if not defined $xstype; | |
353 | } | |
354 | else { | |
355 | $xstype = $_[0]->xstype; | |
356 | } | |
357 | ||
4433194b | 358 | return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup}); |
297f4492 S |
359 | } |
360 | ||
361 | sub _remove { | |
362 | my $self = shift; | |
363 | my $rm = shift; | |
297f4492 | 364 | my $array = shift; |
256eb880 | 365 | my $lookup = shift; |
297f4492 | 366 | |
4433194b S |
367 | # Just fetch the index of the item from the lookup table |
368 | my $index = $lookup->{$rm}; | |
369 | return() if not defined $index; | |
370 | ||
371 | # Nuke the item from storage | |
372 | splice(@$array, $index, 1); | |
373 | ||
374 | # Decrement the storage position of all items thereafter | |
375 | foreach my $key (keys %$lookup) { | |
376 | if ($lookup->{$key} > $index) { | |
377 | $lookup->{$key}--; | |
256eb880 | 378 | } |
297f4492 S |
379 | } |
380 | return(); | |
381 | } | |
382 | ||
383 | =head2 get_typemap | |
384 | ||
385 | Fetches an entry of the TYPEMAP section of the typemap. | |
386 | ||
387 | Mandatory named arguments: The C<ctype> of the entry. | |
388 | ||
7320491e | 389 | Returns the C<ExtUtils::Typemaps::Type> |
297f4492 S |
390 | object for the entry if found. |
391 | ||
392 | =cut | |
393 | ||
394 | sub get_typemap { | |
395 | my $self = shift; | |
396 | my %args = @_; | |
397 | my $ctype = $args{ctype}; | |
398 | croak("Need ctype argument") if not defined $ctype; | |
399 | $ctype = _tidy_type($ctype); | |
400 | ||
256eb880 S |
401 | my $index = $self->{typemap_lookup}{$ctype}; |
402 | return() if not defined $index; | |
403 | return $self->{typemap_section}[$index]; | |
297f4492 S |
404 | } |
405 | ||
406 | =head2 get_inputmap | |
407 | ||
408 | Fetches an entry of the INPUT section of the | |
409 | typemap. | |
410 | ||
411 | Mandatory named arguments: The C<xstype> of the | |
412 | entry. | |
413 | ||
7320491e | 414 | Returns the C<ExtUtils::Typemaps::InputMap> |
297f4492 S |
415 | object for the entry if found. |
416 | ||
417 | =cut | |
418 | ||
419 | sub get_inputmap { | |
420 | my $self = shift; | |
421 | my %args = @_; | |
422 | my $xstype = $args{xstype}; | |
423 | croak("Need xstype argument") if not defined $xstype; | |
424 | ||
7534260c S |
425 | my $index = $self->{input_lookup}{$xstype}; |
426 | return() if not defined $index; | |
427 | return $self->{input_section}[$index]; | |
297f4492 S |
428 | } |
429 | ||
430 | =head2 get_outputmap | |
431 | ||
432 | Fetches an entry of the OUTPUT section of the | |
433 | typemap. | |
434 | ||
435 | Mandatory named arguments: The C<xstype> of the | |
436 | entry. | |
437 | ||
7320491e | 438 | Returns the C<ExtUtils::Typemaps::InputMap> |
297f4492 S |
439 | object for the entry if found. |
440 | ||
441 | =cut | |
442 | ||
443 | sub get_outputmap { | |
444 | my $self = shift; | |
445 | my %args = @_; | |
446 | my $xstype = $args{xstype}; | |
447 | croak("Need xstype argument") if not defined $xstype; | |
448 | ||
080872fe S |
449 | my $index = $self->{output_lookup}{$xstype}; |
450 | return() if not defined $index; | |
451 | return $self->{output_section}[$index]; | |
297f4492 S |
452 | } |
453 | ||
454 | =head2 write | |
455 | ||
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). | |
460 | ||
461 | =cut | |
462 | ||
463 | sub write { | |
464 | my $self = shift; | |
465 | my %args = @_; | |
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; | |
469 | ||
470 | open my $fh, '>', $file | |
471 | or die "Cannot open typemap file '$file' for writing: $!"; | |
472 | print $fh $self->as_string(); | |
473 | close $fh; | |
474 | } | |
475 | ||
476 | =head2 as_string | |
477 | ||
478 | Generates and returns the string form of the typemap. | |
479 | ||
480 | =cut | |
481 | ||
482 | sub as_string { | |
483 | my $self = shift; | |
484 | my $typemap = $self->{typemap_section}; | |
485 | my @code; | |
486 | push @code, "TYPEMAP\n"; | |
487 | foreach my $entry (@$typemap) { | |
488 | # type kind proto | |
9c95e74b | 489 | # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o |
297f4492 S |
490 | push @code, $entry->ctype . "\t" . $entry->xstype |
491 | . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n"; | |
492 | } | |
493 | ||
494 | my $input = $self->{input_section}; | |
495 | if (@$input) { | |
496 | push @code, "\nINPUT\n"; | |
497 | foreach my $entry (@$input) { | |
498 | push @code, $entry->xstype, "\n", $entry->code, "\n"; | |
499 | } | |
500 | } | |
501 | ||
502 | my $output = $self->{output_section}; | |
503 | if (@$output) { | |
504 | push @code, "\nOUTPUT\n"; | |
505 | foreach my $entry (@$output) { | |
506 | push @code, $entry->xstype, "\n", $entry->code, "\n"; | |
507 | } | |
508 | } | |
509 | return join '', @code; | |
510 | } | |
511 | ||
512 | =head2 merge | |
513 | ||
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. | |
516 | ||
517 | Mandatory named argument: C<typemap =E<gt> $another_typemap> | |
518 | ||
519 | Optional argument: C<replace =E<gt> 1> to force replacement | |
520 | of existing typemap entries without warning. | |
521 | ||
522 | =cut | |
523 | ||
524 | sub merge { | |
525 | my $self = shift; | |
526 | my %args = @_; | |
527 | my $typemap = $args{typemap}; | |
7320491e S |
528 | croak("Need ExtUtils::Typemaps as argument") |
529 | if not ref $typemap or not $typemap->isa('ExtUtils::Typemaps'); | |
297f4492 S |
530 | |
531 | my $replace = $args{replace}; | |
532 | ||
533 | # FIXME breaking encapsulation. Add accessor code. | |
534 | # | |
535 | foreach my $entry (@{$typemap->{typemap_section}}) { | |
536 | $self->add_typemap( $entry ); | |
537 | } | |
538 | ||
539 | foreach my $entry (@{$typemap->{input_section}}) { | |
540 | $self->add_inputmap( $entry ); | |
541 | } | |
542 | ||
543 | foreach my $entry (@{$typemap->{output_section}}) { | |
544 | $self->add_outputmap( $entry ); | |
545 | } | |
546 | ||
547 | return 1; | |
548 | } | |
549 | ||
7534260c | 550 | |
99d2ef39 S |
551 | =head2 _get_typemap_hash |
552 | ||
553 | Returns a hash mapping the C types to the XS types: | |
554 | ||
555 | { | |
556 | 'char **' => 'T_PACKEDARRAY', | |
557 | 'bool_t' => 'T_IV', | |
558 | 'AV *' => 'T_AVREF', | |
559 | 'InputStream' => 'T_IN', | |
560 | 'double' => 'T_DOUBLE', | |
561 | # ... | |
562 | } | |
563 | ||
564 | This is documented because it is used by C<ExtUtils::ParseXS>, | |
565 | but it's not intended for general consumption. May be removed | |
566 | at any time. | |
567 | ||
568 | =cut | |
569 | ||
570 | sub _get_typemap_hash { | |
571 | my $self = shift; | |
572 | my $lookup = $self->{typemap_lookup}; | |
573 | my $storage = $self->{typemap_section}; | |
574 | ||
575 | my %rv; | |
576 | foreach my $ctype (keys %$lookup) { | |
577 | $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype; | |
578 | } | |
579 | ||
580 | return \%rv; | |
581 | } | |
582 | ||
583 | =head2 _get_inputmap_hash | |
584 | ||
585 | Returns a hash mapping the XS types (identifiers) to the | |
586 | corresponding INPUT code: | |
587 | ||
588 | { | |
589 | 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg) | |
590 | ', | |
591 | 'T_OUT' => ' $var = IoOFP(sv_2io($arg)) | |
592 | ', | |
593 | 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) { | |
594 | # ... | |
595 | } | |
596 | ||
597 | This is documented because it is used by C<ExtUtils::ParseXS>, | |
598 | but it's not intended for general consumption. May be removed | |
599 | at any time. | |
600 | ||
601 | =cut | |
602 | ||
603 | sub _get_inputmap_hash { | |
604 | my $self = shift; | |
605 | my $lookup = $self->{input_lookup}; | |
606 | my $storage = $self->{input_section}; | |
607 | ||
608 | my %rv; | |
609 | foreach my $xstype (keys %$lookup) { | |
610 | $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code; | |
611 | } | |
612 | ||
613 | return \%rv; | |
614 | } | |
615 | ||
616 | ||
617 | =head2 _get_outputmap_hash | |
618 | ||
619 | Returns a hash mapping the XS types (identifiers) to the | |
620 | corresponding OUTPUT code: | |
621 | ||
622 | { | |
623 | 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(), | |
624 | $var.context.value().size()); | |
625 | ', | |
626 | 'T_OUT' => ' { | |
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))); | |
630 | else | |
631 | $arg = &PL_sv_undef; | |
632 | } | |
633 | ', | |
634 | # ... | |
635 | } | |
636 | ||
637 | This is documented because it is used by C<ExtUtils::ParseXS>, | |
638 | but it's not intended for general consumption. May be removed | |
639 | at any time. | |
640 | ||
641 | =cut | |
642 | ||
643 | sub _get_outputmap_hash { | |
644 | my $self = shift; | |
645 | my $lookup = $self->{output_lookup}; | |
646 | my $storage = $self->{output_section}; | |
647 | ||
648 | my %rv; | |
649 | foreach my $xstype (keys %$lookup) { | |
650 | $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code; | |
651 | } | |
652 | ||
653 | return \%rv; | |
654 | } | |
655 | ||
656 | ||
657 | ||
658 | ||
7534260c S |
659 | # make sure that the provided types wouldn't collide with what's |
660 | # in the object already. | |
297f4492 S |
661 | sub validate { |
662 | my $self = shift; | |
663 | my %args = @_; | |
664 | ||
256eb880 S |
665 | if ( exists $args{ctype} |
666 | and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} ) | |
667 | { | |
668 | croak("Multiple definition of ctype '$args{ctype}' in TYPEMAP section"); | |
297f4492 S |
669 | } |
670 | ||
7534260c S |
671 | if ( exists $args{inputmap_xstype} |
672 | and exists $self->{input_lookup}{$args{inputmap_xstype}} ) | |
673 | { | |
080872fe | 674 | croak("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section"); |
297f4492 S |
675 | } |
676 | ||
080872fe S |
677 | if ( exists $args{outputmap_xstype} |
678 | and exists $self->{output_lookup}{$args{outputmap_xstype}} ) | |
679 | { | |
680 | croak("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section"); | |
297f4492 S |
681 | } |
682 | ||
683 | return 1; | |
684 | } | |
685 | ||
686 | sub _parse { | |
687 | my $self = shift; | |
688 | my $stringref = shift; | |
689 | my $filename = shift; | |
690 | $filename = '<string>' if not defined $filename; | |
691 | ||
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'; | |
696 | my $lineno = 0; | |
697 | my $junk = ""; | |
698 | my $current = \$junk; | |
297f4492 S |
699 | my @input_expr; |
700 | my @output_expr; | |
701 | while ($$stringref =~ /^(.*)$/gcm) { | |
702 | local $_ = $1; | |
703 | ++$lineno; | |
704 | chomp; | |
705 | next if /^\s*#/; | |
706 | if (/^INPUT\s*$/) { | |
707 | $section = 'input'; | |
708 | $current = \$junk; | |
709 | next; | |
710 | } | |
711 | elsif (/^OUTPUT\s*$/) { | |
712 | $section = 'output'; | |
713 | $current = \$junk; | |
714 | next; | |
715 | } | |
716 | elsif (/^TYPEMAP\s*$/) { | |
717 | $section = 'typemap'; | |
718 | $current = \$junk; | |
719 | next; | |
720 | } | |
721 | ||
722 | if ($section eq 'typemap') { | |
723 | my $line = $_; | |
724 | s/^\s+//; s/\s+$//; | |
725 | next if /^#/ or /^$/; | |
9c95e74b | 726 | my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o |
297f4492 S |
727 | or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"), |
728 | next; | |
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); | |
256eb880 S |
734 | $self->add_typemap( |
735 | ExtUtils::Typemaps::Type->new( | |
736 | xstype => $kind, proto => $proto, ctype => $type | |
737 | ) | |
297f4492 S |
738 | ); |
739 | } elsif (/^\s/) { | |
740 | $$current .= $$current eq '' ? $_ : "\n".$_; | |
741 | } elsif (/^$/) { | |
742 | next; | |
743 | } elsif ($section eq 'input') { | |
744 | s/\s+$//; | |
745 | push @input_expr, {xstype => $_, code => ''}; | |
746 | $current = \$input_expr[-1]{code}; | |
747 | } else { # output section | |
748 | s/\s+$//; | |
749 | push @output_expr, {xstype => $_, code => ''}; | |
750 | $current = \$output_expr[-1]{code}; | |
751 | } | |
752 | ||
753 | } # end while lines | |
754 | ||
7534260c S |
755 | foreach my $inexpr (@input_expr) { |
756 | $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr) ); | |
757 | } | |
080872fe S |
758 | foreach my $outexpr (@output_expr) { |
759 | $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr) ); | |
760 | } | |
7534260c | 761 | |
080872fe | 762 | return 1; |
297f4492 S |
763 | } |
764 | ||
765 | # taken from ExtUtils::ParseXS | |
766 | sub _tidy_type { | |
767 | local $_ = shift; | |
768 | ||
769 | # rationalise any '*' by joining them into bunches and removing whitespace | |
770 | s#\s*(\*+)\s*#$1#g; | |
771 | s#(\*+)# $1 #g ; | |
772 | ||
773 | # trim leading & trailing whitespace | |
774 | s/^\s+//; s/\s+$//; | |
775 | ||
776 | # change multiple whitespace into a single space | |
777 | s/\s+/ /g; | |
778 | ||
779 | $_; | |
780 | } | |
781 | ||
782 | ||
783 | # taken from ExtUtils::ParseXS | |
784 | sub _valid_proto_string { | |
785 | my $string = shift; | |
9c95e74b | 786 | if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) { |
297f4492 S |
787 | return $string; |
788 | } | |
789 | ||
790 | return 0 ; | |
791 | } | |
792 | ||
793 | # taken from ExtUtils::ParseXS (C_string) | |
794 | sub _escape_backslashes { | |
795 | my $string = shift; | |
796 | $string =~ s[\\][\\\\]g; | |
797 | $string; | |
798 | } | |
799 | ||
800 | =head1 CAVEATS | |
801 | ||
297f4492 S |
802 | Inherits some evil code from C<ExtUtils::ParseXS>. |
803 | ||
297f4492 S |
804 | =head1 SEE ALSO |
805 | ||
806 | The parser is heavily inspired from the one in L<ExtUtils::ParseXS>. | |
807 | ||
808 | For details on typemaps: L<perlxstut>, L<perlxs>. | |
809 | ||
810 | =head1 AUTHOR | |
811 | ||
812 | Steffen Mueller C<<smueller@cpan.org>> | |
813 | ||
814 | =head1 COPYRIGHT & LICENSE | |
815 | ||
0b19625b | 816 | Copyright 2009-2011 Steffen Mueller |
297f4492 S |
817 | |
818 | This program is free software; you can redistribute it and/or | |
819 | modify it under the same terms as Perl itself. | |
820 | ||
821 | =cut | |
822 | ||
823 | 1; | |
824 |