Commit | Line | Data |
2304df62 |
1 | #!./miniperl |
75f92628 |
2 | |
3 | =head1 NAME |
4 | |
5 | xsubpp - compiler to convert Perl XS code into C code |
6 | |
7 | =head1 SYNOPSIS |
8 | |
8e07c86e |
9 | B<xsubpp> [B<-v>] [B<-C++>] [B<-except>] [B<-s pattern>] [B<-typemap typemap>]... file.xs |
75f92628 |
10 | |
11 | =head1 DESCRIPTION |
12 | |
13 | I<xsubpp> will compile XS code into C code by embedding the constructs |
14 | necessary to let C functions manipulate Perl values and creates the glue |
15 | necessary to let Perl access those functions. The compiler uses typemaps to |
16 | determine how to map C function parameters and variables to Perl values. |
17 | |
18 | The compiler will search for typemap files called I<typemap>. It will use |
19 | the following search path to find default typemaps, with the rightmost |
20 | typemap taking precedence. |
21 | |
22 | ../../../typemap:../../typemap:../typemap:typemap |
23 | |
24 | =head1 OPTIONS |
25 | |
26 | =over 5 |
27 | |
28 | =item B<-C++> |
29 | |
30 | Adds ``extern "C"'' to the C code. |
31 | |
32 | |
33 | =item B<-except> |
34 | |
35 | Adds exception handling stubs to the C code. |
36 | |
37 | =item B<-typemap typemap> |
38 | |
39 | Indicates that a user-supplied typemap should take precedence over the |
40 | default typemaps. This option may be used multiple times, with the last |
41 | typemap having the highest precedence. |
42 | |
8e07c86e |
43 | =item B<-v> |
44 | |
45 | Prints the I<xsubpp> version number to standard output, then exits. |
46 | |
75f92628 |
47 | =back |
48 | |
49 | =head1 ENVIRONMENT |
50 | |
51 | No environment variables are used. |
52 | |
53 | =head1 AUTHOR |
54 | |
55 | Larry Wall |
56 | |
f06db76b |
57 | =head1 MODIFICATION HISTORY |
58 | |
8e07c86e |
59 | See the file F<changes.pod>. |
e50aee73 |
60 | |
75f92628 |
61 | =head1 SEE ALSO |
62 | |
e50aee73 |
63 | perl(1), perlapi(1) |
75f92628 |
64 | |
65 | =cut |
93a17b20 |
66 | |
f06db76b |
67 | # Global Constants |
8e07c86e |
68 | $XSUBPP_version = "1.922"; |
69 | require 5.001; |
f06db76b |
70 | |
8e07c86e |
71 | $usage = "Usage: xsubpp [-v] [-C++] [-except] [-s pattern] [-typemap typemap]... file.xs\n"; |
93a17b20 |
72 | |
8e07c86e |
73 | $except = ""; |
74 | SWITCH: while (@ARGV and $ARGV[0] =~ /^-./) { |
93a17b20 |
75 | $flag = shift @ARGV; |
e50aee73 |
76 | $flag =~ s/^-// ; |
8990e307 |
77 | $spat = shift, next SWITCH if $flag eq 's'; |
78 | $cplusplus = 1, next SWITCH if $flag eq 'C++'; |
8e07c86e |
79 | $except = " TRY", next SWITCH if $flag eq 'except'; |
8990e307 |
80 | push(@tm,shift), next SWITCH if $flag eq 'typemap'; |
8e07c86e |
81 | (print "xsubpp version $XSUBPP_version\n"), exit |
82 | if $flag eq 'v'; |
93a17b20 |
83 | die $usage; |
84 | } |
8990e307 |
85 | @ARGV == 1 or die $usage; |
e50aee73 |
86 | chomp($pwd = `pwd`); |
748a9306 |
87 | # Check for error message from VMS |
88 | if ($pwd =~ /unrecognized command verb/) { $Is_VMS = 1; $pwd = $ENV{DEFAULT} } |
c2960299 |
89 | ($dir, $filename) = $ARGV[0] =~ m#(.*)/(.*)# |
90 | or ($dir, $filename) = $ARGV[0] =~ m#(.*[>\]])(.*)# |
8990e307 |
91 | or ($dir, $filename) = ('.', $ARGV[0]); |
92 | chdir($dir); |
93a17b20 |
93 | |
f06db76b |
94 | sub TrimWhitespace |
95 | { |
96 | $_[0] =~ s/^\s+|\s+$//go ; |
97 | } |
98 | |
99 | sub TidyType |
100 | { |
101 | local ($_) = @_ ; |
102 | |
103 | # rationalise any '*' by joining them into bunches and removing whitespace |
104 | s#\s*(\*+)\s*#$1#g; |
e50aee73 |
105 | s#(\*+)# $1 #g ; |
f06db76b |
106 | |
107 | # change multiple whitespace into a single space |
108 | s/\s+/ /g ; |
109 | |
110 | # trim leading & trailing whitespace |
111 | TrimWhitespace($_) ; |
112 | |
113 | $_ ; |
114 | } |
115 | |
93a17b20 |
116 | $typemap = shift @ARGV; |
8990e307 |
117 | foreach $typemap (@tm) { |
118 | die "Can't find $typemap in $pwd\n" unless -r $typemap; |
93a17b20 |
119 | } |
748a9306 |
120 | unshift @tm, qw(../../../../lib/ExtUtils/typemap ../../../lib/ExtUtils/typemap |
121 | ../../lib/ExtUtils/typemap ../../../typemap ../../typemap |
122 | ../typemap typemap); |
8990e307 |
123 | foreach $typemap (@tm) { |
f06db76b |
124 | next unless -e $typemap ; |
125 | # skip directories, binary files etc. |
126 | warn("Warning: ignoring non-text typemap file '$typemap'\n"), next |
127 | unless -T $typemap ; |
128 | open(TYPEMAP, $typemap) |
129 | or warn ("Warning: could not open typemap file '$typemap': $!\n"), next; |
e50aee73 |
130 | $mode = 'Typemap'; |
c2960299 |
131 | $junk = "" ; |
8990e307 |
132 | $current = \$junk; |
133 | while (<TYPEMAP>) { |
e50aee73 |
134 | next if /^\s*#/; |
8e07c86e |
135 | if (/^INPUT\s*$/) { $mode = 'Input'; $current = \$junk; next; } |
136 | if (/^OUTPUT\s*$/) { $mode = 'Output'; $current = \$junk; next; } |
137 | if (/^TYPEMAP\s*$/) { $mode = 'Typemap'; $current = \$junk; next; } |
e50aee73 |
138 | if ($mode eq 'Typemap') { |
139 | chomp; |
f06db76b |
140 | my $line = $_ ; |
141 | TrimWhitespace($_) ; |
142 | # skip blank lines and comment lines |
143 | next if /^$/ or /^#/ ; |
8e07c86e |
144 | my($type,$kind) = /^\s*(.*?\S)\s+(\S+)\s*$/ or |
145 | warn("Warning: File '$typemap' Line $. '$line' TYPEMAP entry needs 2 columns\n"), next; |
146 | $type_kind{TidyType($type)} = $kind ; |
147 | } |
148 | elsif (/^\s/) { |
149 | $$current .= $_; |
463ee0b2 |
150 | } |
e50aee73 |
151 | elsif ($mode eq 'Input') { |
8e07c86e |
152 | s/\s+$//; |
153 | $input_expr{$_} = ''; |
154 | $current = \$input_expr{$_}; |
93a17b20 |
155 | } |
8990e307 |
156 | else { |
8e07c86e |
157 | s/\s+$//; |
158 | $output_expr{$_} = ''; |
159 | $current = \$output_expr{$_}; |
93a17b20 |
160 | } |
8990e307 |
161 | } |
162 | close(TYPEMAP); |
163 | } |
93a17b20 |
164 | |
8990e307 |
165 | foreach $key (keys %input_expr) { |
166 | $input_expr{$key} =~ s/\n+$//; |
167 | } |
93a17b20 |
168 | |
8e07c86e |
169 | $END = "!End!\n\n"; # "impossible" keyword (multiple newline) |
170 | |
171 | # Match an XS keyword |
172 | $BLOCK_re= "\\s*(REQUIRE|BOOT|CASE|PREINIT|INPUT|INIT|CODE|PPCODE|OUTPUT|CLEANUP|ALIAS|$END)\\s*:"; |
173 | |
174 | # Input: ($_, @line) == unparsed input. |
175 | # Output: ($_, @line) == (rest of line, following lines). |
176 | # Return: the matched keyword if found, otherwise 0 |
177 | sub check_keyword { |
178 | $_ = shift(@line) while !/\S/ && @line; |
179 | s/^(\s*)($_[0])\s*:\s*(?:#.*)?/$1/s && $2; |
180 | } |
181 | |
182 | |
183 | sub print_section { |
184 | $_ = shift(@line) while !/\S/ && @line; |
185 | for (; defined($_) && !/^$BLOCK_re/o; $_ = shift(@line)) { |
186 | print "$_\n"; |
187 | } |
188 | } |
189 | |
190 | sub CASE_handler { |
191 | blurt ("Error: `CASE:' after unconditional `CASE:'") |
192 | if $condnum && $cond eq ''; |
193 | $cond = $_; |
194 | TrimWhitespace($cond); |
195 | print " ", ($condnum++ ? " else" : ""), ($cond ? " if ($cond)\n" : "\n"); |
196 | $_ = '' ; |
197 | } |
198 | |
199 | sub INPUT_handler { |
200 | for (; !/^$BLOCK_re/o; $_ = shift(@line)) { |
201 | last if /^\s*NOT_IMPLEMENTED_YET/; |
202 | next unless /\S/; # skip blank lines |
203 | |
204 | TrimWhitespace($_) ; |
205 | my $line = $_ ; |
206 | |
207 | # remove trailing semicolon if no initialisation |
208 | s/\s*;$//g unless /=/ ; |
209 | |
210 | # check for optional initialisation code |
211 | my $var_init = '' ; |
212 | $var_init = $1 if s/\s*(=.*)$//s ; |
213 | $var_init =~ s/"/\\"/g; |
214 | |
215 | s/\s+/ /g; |
216 | my ($var_type, $var_addr, $var_name) = /^(.*?[^& ]) *(\&?) *\b(\w+)$/s |
217 | or blurt("Error: invalid argument declaration '$line'"), next; |
218 | |
219 | # Check for duplicate definitions |
220 | blurt ("Error: duplicate definition of argument '$var_name' ignored"), next |
221 | if $arg_list{$var_name} ++ ; |
222 | |
223 | $thisdone |= $var_name eq "THIS"; |
224 | $retvaldone |= $var_name eq "RETVAL"; |
225 | $var_types{$var_name} = $var_type; |
226 | print "\t" . &map_type($var_type); |
227 | $var_num = $args_match{$var_name}; |
228 | if ($var_addr) { |
229 | $var_addr{$var_name} = 1; |
230 | $func_args =~ s/\b($var_name)\b/&$1/; |
231 | } |
232 | if ($var_init =~ /^=\s*NO_INIT\s*;?\s*$/) { |
233 | print "\t$var_name;\n"; |
234 | } elsif ($var_init =~ /\S/) { |
235 | &output_init($var_type, $var_num, "$var_name $var_init"); |
236 | } elsif ($var_num) { |
237 | # generate initialization code |
238 | &generate_init($var_type, $var_num, $var_name); |
239 | } else { |
240 | print ";\n"; |
241 | } |
242 | } |
243 | } |
244 | |
245 | sub OUTPUT_handler { |
246 | for (; !/^$BLOCK_re/o; $_ = shift(@line)) { |
247 | next unless /\S/; |
248 | my ($outarg, $outcode) = /^\s*(\S+)\s*(.*?)\s*$/s ; |
249 | blurt ("Error: duplicate OUTPUT argument '$outarg' ignored"), next |
250 | if $outargs{$outarg} ++ ; |
251 | if (!$gotRETVAL and $outarg eq 'RETVAL') { |
252 | # deal with RETVAL last |
253 | $RETVAL_code = $outcode ; |
254 | $gotRETVAL = 1 ; |
255 | next ; |
256 | } |
257 | blurt ("Error: OUTPUT $outarg not an argument"), next |
258 | unless defined($args_match{$outarg}); |
259 | blurt("Error: No input definition for OUTPUT argument '$outarg' - ignored"), next |
260 | unless defined $var_types{$outarg} ; |
261 | if ($outcode) { |
262 | print "\t$outcode\n"; |
263 | } else { |
264 | $var_num = $args_match{$outarg}; |
265 | &generate_output($var_types{$outarg}, $var_num, $outarg); |
266 | } |
267 | } |
268 | } |
269 | |
270 | sub GetAliases |
271 | { |
272 | my ($line) = @_ ; |
273 | my ($orig) = $line ; |
274 | my ($alias) ; |
275 | my ($value) ; |
276 | |
277 | # Parse alias definitions |
278 | # format is |
279 | # alias = value alias = value ... |
280 | |
281 | while ($line =~ s/^\s*([\w:]+)\s*=\s*(\w+)\s*//) { |
282 | $alias = $1 ; |
283 | $orig_alias = $alias ; |
284 | $value = $2 ; |
285 | |
286 | # check for optional package definition in the alias |
287 | $alias = $Packprefix . $alias if $alias !~ /::/ ; |
288 | |
289 | # check for duplicate alias name & duplicate value |
290 | Warn("Warning: Ignoring duplicate alias '$orig_alias'") |
291 | if defined $XsubAliases{$pname}{$alias} ; |
292 | |
293 | Warn("Warning: Aliases '$orig_alias' and '$XsubAliasValues{$pname}{$value}' have identical values") |
294 | if $XsubAliasValues{$pname}{$value} ; |
295 | |
296 | $XsubAliases{$pname}{$alias} = $value ; |
297 | $XsubAliasValues{$pname}{$value} = $orig_alias ; |
298 | } |
299 | |
300 | blurt("Error: Cannot parse ALIAS definitions from '$orig'") |
301 | if $line ; |
302 | } |
303 | |
304 | sub ALIAS_handler |
305 | { |
306 | for (; !/^$BLOCK_re/o; $_ = shift(@line)) { |
307 | next unless /\S/; |
308 | TrimWhitespace($_) ; |
309 | GetAliases($_) if $_ ; |
310 | } |
311 | } |
312 | |
313 | sub REQUIRE_handler |
314 | { |
315 | # the rest of the current line should contain a version number |
316 | my ($Ver) = $_ ; |
317 | |
318 | TrimWhitespace($Ver) ; |
319 | |
320 | death ("Error: REQUIRE expects a version number") |
321 | unless $Ver ; |
322 | |
323 | # check that the version number is of the form n.n |
324 | death ("Error: REQUIRE: expected a number, got '$Ver'") |
325 | unless $Ver =~ /^\d+(\.\d*)?/ ; |
326 | |
327 | death ("Error: xsubpp $Ver (or better) required--this is only $XSUBPP_version.") |
328 | unless $XSUBPP_version >= $Ver ; |
329 | } |
330 | |
331 | sub check_cpp { |
332 | my @cpp = grep(/^\#\s*(?:if|e\w+)/, @line); |
333 | if (@cpp) { |
334 | my ($cpp, $cpplevel); |
335 | for $cpp (@cpp) { |
336 | if ($cpp =~ /^\#\s*if/) { |
337 | $cpplevel++; |
338 | } elsif (!$cpplevel) { |
339 | Warn("Warning: #else/elif/endif without #if in this function"); |
340 | return; |
341 | } elsif ($cpp =~ /^\#\s*endif/) { |
342 | $cpplevel--; |
343 | } |
344 | } |
345 | Warn("Warning: #if without #endif in this function") if $cpplevel; |
346 | } |
347 | } |
348 | |
349 | |
8990e307 |
350 | sub Q { |
e50aee73 |
351 | my($text) = @_; |
8990e307 |
352 | $text =~ tr/#//d; |
2304df62 |
353 | $text =~ s/\[\[/{/g; |
354 | $text =~ s/\]\]/}/g; |
8990e307 |
355 | $text; |
93a17b20 |
356 | } |
357 | |
c2960299 |
358 | open(F, $filename) or die "cannot open $filename: $!\n"; |
359 | |
f06db76b |
360 | # Identify the version of xsubpp used |
f06db76b |
361 | print <<EOM ; |
e50aee73 |
362 | /* |
363 | * This file was generated automatically by xsubpp version $XSUBPP_version from the |
364 | * contents of $filename. Don't edit this file, edit $filename instead. |
365 | * |
366 | * ANY CHANGES MADE HERE WILL BE LOST! |
f06db76b |
367 | * |
368 | */ |
e50aee73 |
369 | |
f06db76b |
370 | EOM |
371 | |
372 | |
93a17b20 |
373 | while (<F>) { |
e50aee73 |
374 | last if ($Module, $Package, $Prefix) = |
375 | /^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/; |
a0d0e21e |
376 | print $_; |
93a17b20 |
377 | } |
e50aee73 |
378 | &Exit unless defined $_; |
379 | |
380 | my $lastline = $_; |
381 | my $lastline_no = $.; |
93a17b20 |
382 | |
e50aee73 |
383 | |
384 | # Read next xsub into @line from ($lastline, <F>). |
2304df62 |
385 | sub fetch_para { |
386 | # parse paragraph |
387 | @line = (); |
c2960299 |
388 | @line_no = () ; |
e50aee73 |
389 | return 0 unless defined $lastline; |
390 | |
391 | if ($lastline =~ |
392 | /^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/) { |
393 | $Module = $1; |
8e07c86e |
394 | $Package = defined($2) ? $2 : ''; # keep -w happy |
395 | $Prefix = defined($3) ? $3 : ''; # keep -w happy |
e50aee73 |
396 | ($Module_cname = $Module) =~ s/\W/_/g; |
8e07c86e |
397 | ($Packid = $Package) =~ tr/:/_/; |
e50aee73 |
398 | $Packprefix = $Package; |
8e07c86e |
399 | $Packprefix .= "::" if $Packprefix ne ""; |
2304df62 |
400 | $lastline = ""; |
e50aee73 |
401 | } |
402 | |
403 | for(;;) { |
404 | if ($lastline !~ /^\s*#/ || |
8e07c86e |
405 | $lastline =~ /^#[ \t]*(?:(?:if|ifn?def|else|elif|endif|define|undef|pragma)\b|include\s*["<].*[>"])/) { |
e50aee73 |
406 | last if $lastline =~ /^\S/ && @line && $line[-1] eq ""; |
407 | push(@line, $lastline); |
408 | push(@line_no, $lastline_no) ; |
93a17b20 |
409 | } |
e50aee73 |
410 | |
411 | # Read next line and continuation lines |
412 | last unless defined($lastline = <F>); |
413 | $lastline_no = $.; |
414 | my $tmp_line; |
415 | $lastline .= $tmp_line |
8e07c86e |
416 | while ($lastline =~ /\\$/ && defined($tmp_line = <F>)); |
e50aee73 |
417 | |
8e07c86e |
418 | chomp $lastline; |
e50aee73 |
419 | $lastline =~ s/^\s+$//; |
2304df62 |
420 | } |
e50aee73 |
421 | pop(@line), pop(@line_no) while @line && $line[-1] eq ""; |
e50aee73 |
422 | 1; |
2304df62 |
423 | } |
93a17b20 |
424 | |
c2960299 |
425 | PARAGRAPH: |
8e07c86e |
426 | while (fetch_para()) { |
e50aee73 |
427 | # Print initial preprocessor statements and blank lines |
428 | print shift(@line), "\n" |
429 | while @line && $line[0] !~ /^[^\#]/; |
430 | |
431 | next PARAGRAPH unless @line; |
432 | |
433 | death ("Code is not inside a function") |
434 | if $line[0] =~ /^\s/; |
435 | |
2304df62 |
436 | # initialize info arrays |
437 | undef(%args_match); |
438 | undef(%var_types); |
439 | undef(%var_addr); |
440 | undef(%defaults); |
441 | undef($class); |
442 | undef($static); |
443 | undef($elipsis); |
f06db76b |
444 | undef($wantRETVAL) ; |
445 | undef(%arg_list) ; |
2304df62 |
446 | |
8e07c86e |
447 | $_ = shift(@line); |
448 | if (check_keyword("REQUIRE")) { |
449 | REQUIRE_handler() ; |
450 | next PARAGRAPH unless @line ; |
451 | $_ = shift(@line); |
452 | } |
c2960299 |
453 | |
8e07c86e |
454 | if (check_keyword("BOOT")) { |
455 | &check_cpp; |
456 | push (@BootCode, $_, @line, "") ; |
c2960299 |
457 | next PARAGRAPH ; |
a0d0e21e |
458 | } |
c2960299 |
459 | |
8e07c86e |
460 | |
461 | # extract return type, function name and arguments |
462 | my($ret_type) = TidyType($_); |
463 | |
c2960299 |
464 | # a function definition needs at least 2 lines |
465 | blurt ("Error: Function definition too short '$ret_type'"), next PARAGRAPH |
466 | unless @line ; |
467 | |
8e07c86e |
468 | $static = 1 if $ret_type =~ s/^static\s+//; |
469 | |
2304df62 |
470 | $func_header = shift(@line); |
c2960299 |
471 | blurt ("Error: Cannot parse function definition from '$func_header'"), next PARAGRAPH |
8e07c86e |
472 | unless $func_header =~ /^(?:([\w:]*)::)?(\w+)\s*\(\s*(.*?)\s*\)\s*$/s; |
c2960299 |
473 | |
8e07c86e |
474 | ($class, $func_name, $orig_args) = ($1, $2, $3) ; |
2304df62 |
475 | ($pname = $func_name) =~ s/^($Prefix)?/$Packprefix/; |
c2960299 |
476 | |
477 | # Check for duplicate function definition |
8e07c86e |
478 | if (defined $Func_name{"${Packid}_$func_name"} ) { |
479 | Warn("Warning: duplicate function definition '$func_name' detected") |
480 | } |
481 | else { |
482 | push(@Func_name, "${Packid}_$func_name"); |
483 | push(@Func_pname, $pname); |
484 | } |
c2960299 |
485 | $Func_name{"${Packid}_$func_name"} ++ ; |
486 | |
2304df62 |
487 | @args = split(/\s*,\s*/, $orig_args); |
a0d0e21e |
488 | if (defined($class)) { |
8e07c86e |
489 | my $arg0 = (defined($static) ? "CLASS" : "THIS"); |
490 | unshift(@args, $arg0); |
491 | ($orig_args = "$arg0, $orig_args") =~ s/^$arg0, $/$arg0/; |
2304df62 |
492 | } |
493 | $orig_args =~ s/"/\\"/g; |
494 | $min_args = $num_args = @args; |
495 | foreach $i (0..$num_args-1) { |
496 | if ($args[$i] =~ s/\.\.\.//) { |
497 | $elipsis = 1; |
498 | $min_args--; |
c2960299 |
499 | if ($args[$i] eq '' && $i == $num_args - 1) { |
2304df62 |
500 | pop(@args); |
501 | last; |
502 | } |
503 | } |
8e07c86e |
504 | if ($args[$i] =~ /^([^=]*[^\s=])\s*=\s*(.*)/s) { |
2304df62 |
505 | $min_args--; |
506 | $args[$i] = $1; |
507 | $defaults{$args[$i]} = $2; |
508 | $defaults{$args[$i]} =~ s/"/\\"/g; |
509 | } |
510 | } |
a0d0e21e |
511 | if (defined($class)) { |
2304df62 |
512 | $func_args = join(", ", @args[1..$#args]); |
513 | } else { |
514 | $func_args = join(", ", @args); |
515 | } |
516 | @args_match{@args} = 1..@args; |
517 | |
8e07c86e |
518 | $PPCODE = grep(/^\s*PPCODE\s*:/, @line); |
519 | $ALIAS = grep(/^\s*ALIAS\s*:/, @line); |
520 | |
2304df62 |
521 | # print function header |
a0d0e21e |
522 | print Q<<"EOF"; |
523 | #XS(XS_${Packid}_$func_name) |
2304df62 |
524 | #[[ |
a0d0e21e |
525 | # dXSARGS; |
93a17b20 |
526 | EOF |
8e07c86e |
527 | print Q<<"EOF" if $ALIAS ; |
528 | # dXSI32; |
529 | EOF |
2304df62 |
530 | if ($elipsis) { |
8e07c86e |
531 | $cond = ($min_args ? qq(items < $min_args) : 0); |
2304df62 |
532 | } |
533 | elsif ($min_args == $num_args) { |
534 | $cond = qq(items != $min_args); |
535 | } |
536 | else { |
537 | $cond = qq(items < $min_args || items > $num_args); |
538 | } |
8990e307 |
539 | |
2304df62 |
540 | print Q<<"EOF" if $except; |
541 | # char errbuf[1024]; |
542 | # *errbuf = '\0'; |
543 | EOF |
544 | |
8e07c86e |
545 | if ($ALIAS) |
546 | { print Q<<"EOF" if $cond } |
547 | # if ($cond) |
548 | # croak("Usage: %s($orig_args)", GvNAME(CvGV(cv))); |
549 | EOF |
550 | else |
551 | { print Q<<"EOF" if $cond } |
552 | # if ($cond) |
8990e307 |
553 | # croak("Usage: $pname($orig_args)"); |
93a17b20 |
554 | EOF |
555 | |
a0d0e21e |
556 | print Q<<"EOF" if $PPCODE; |
557 | # SP -= items; |
558 | EOF |
559 | |
2304df62 |
560 | # Now do a block of some sort. |
93a17b20 |
561 | |
2304df62 |
562 | $condnum = 0; |
8e07c86e |
563 | $cond = ''; # last CASE: condidional |
564 | push(@line, "$END:"); |
565 | push(@line_no, $line_no[-1]); |
566 | $_ = ''; |
567 | &check_cpp; |
2304df62 |
568 | while (@line) { |
8e07c86e |
569 | &CASE_handler if check_keyword("CASE"); |
570 | print Q<<"EOF"; |
571 | # $except [[ |
93a17b20 |
572 | EOF |
573 | |
574 | # do initialization of input variables |
575 | $thisdone = 0; |
576 | $retvaldone = 0; |
463ee0b2 |
577 | $deferred = ""; |
c2960299 |
578 | %arg_list = () ; |
579 | $gotRETVAL = 0; |
f06db76b |
580 | |
8e07c86e |
581 | &INPUT_handler; |
582 | my $kwd; |
583 | while ($kwd = check_keyword("INPUT|PREINIT")) { |
584 | if ($kwd eq 'PREINIT') { &print_section; } else { &INPUT_handler; } |
93a17b20 |
585 | } |
a0d0e21e |
586 | if (!$thisdone && defined($class)) { |
587 | if (defined($static)) { |
588 | print "\tchar *"; |
589 | $var_types{"CLASS"} = "char *"; |
590 | &generate_init("char *", 1, "CLASS"); |
591 | } |
592 | else { |
93a17b20 |
593 | print "\t$class *"; |
594 | $var_types{"THIS"} = "$class *"; |
595 | &generate_init("$class *", 1, "THIS"); |
a0d0e21e |
596 | } |
93a17b20 |
597 | } |
598 | |
599 | # do code |
600 | if (/^\s*NOT_IMPLEMENTED_YET/) { |
463ee0b2 |
601 | print "\ncroak(\"$pname: not implemented yet\");\n"; |
93a17b20 |
602 | } else { |
603 | if ($ret_type ne "void") { |
604 | print "\t" . &map_type($ret_type) . "\tRETVAL;\n" |
605 | if !$retvaldone; |
606 | $args_match{"RETVAL"} = 0; |
607 | $var_types{"RETVAL"} = $ret_type; |
608 | } |
8e07c86e |
609 | print $deferred; |
610 | while ($kwd = check_keyword("INIT|ALIAS")) { |
611 | if ($kwd eq 'INIT') { |
612 | &print_section |
613 | } |
614 | else { |
615 | ALIAS_handler |
616 | } |
617 | } |
618 | |
619 | if (check_keyword("PPCODE")) { |
620 | &print_section; |
621 | death ("PPCODE must be last thing") if @line; |
a0d0e21e |
622 | print "\tPUTBACK;\n\treturn;\n"; |
8e07c86e |
623 | } elsif (check_keyword("CODE")) { |
624 | &print_section; |
a0d0e21e |
625 | } elsif ($func_name eq "DESTROY") { |
a0d0e21e |
626 | print "\n\t"; |
8e07c86e |
627 | print "delete THIS;\n"; |
93a17b20 |
628 | } else { |
629 | print "\n\t"; |
630 | if ($ret_type ne "void") { |
463ee0b2 |
631 | print "RETVAL = "; |
e50aee73 |
632 | $wantRETVAL = 1; |
93a17b20 |
633 | } |
634 | if (defined($static)) { |
a0d0e21e |
635 | if ($func_name =~ /^new/) { |
636 | $func_name = "$class"; |
8e07c86e |
637 | } else { |
638 | print "${class}::"; |
a0d0e21e |
639 | } |
93a17b20 |
640 | } elsif (defined($class)) { |
641 | print "THIS->"; |
642 | } |
e50aee73 |
643 | $func_name =~ s/^($spat)// |
644 | if defined($spat); |
93a17b20 |
645 | print "$func_name($func_args);\n"; |
93a17b20 |
646 | } |
647 | } |
648 | |
649 | # do output variables |
8e07c86e |
650 | $gotRETVAL = 0; |
651 | undef $RETVAL_code ; |
652 | undef %outargs ; |
653 | &OUTPUT_handler while check_keyword("OUTPUT"); |
f06db76b |
654 | |
655 | # all OUTPUT done, so now push the return value on the stack |
8e07c86e |
656 | if ($gotRETVAL && $RETVAL_code) { |
657 | print "\t$RETVAL_code\n"; |
658 | } elsif ($gotRETVAL || $wantRETVAL) { |
659 | &generate_output($ret_type, 0, 'RETVAL'); |
660 | } |
f06db76b |
661 | |
93a17b20 |
662 | # do cleanup |
8e07c86e |
663 | &print_section while check_keyword("CLEANUP"); |
664 | |
93a17b20 |
665 | # print function trailer |
8e07c86e |
666 | print Q<<EOF; |
2304df62 |
667 | # ]] |
8e07c86e |
668 | EOF |
669 | print Q<<EOF if $except; |
8990e307 |
670 | # BEGHANDLERS |
671 | # CATCHALL |
672 | # sprintf(errbuf, "%s: %s\\tpropagated", Xname, Xreason); |
673 | # ENDHANDLERS |
93a17b20 |
674 | EOF |
8e07c86e |
675 | if (check_keyword("CASE")) { |
676 | blurt ("Error: No `CASE:' at top of function") |
677 | unless $condnum; |
678 | $_ = "CASE: $_"; # Restore CASE: label |
679 | next; |
8990e307 |
680 | } |
8e07c86e |
681 | last if $_ eq "$END:"; |
682 | death(/^$BLOCK_re/o ? "Misplaced `$1:'" : "Junk at end of function"); |
2304df62 |
683 | } |
a0d0e21e |
684 | |
2304df62 |
685 | print Q<<EOF if $except; |
686 | # if (errbuf[0]) |
687 | # croak(errbuf); |
688 | EOF |
a0d0e21e |
689 | |
690 | print Q<<EOF unless $PPCODE; |
691 | # XSRETURN(1); |
692 | EOF |
693 | |
2304df62 |
694 | print Q<<EOF; |
2304df62 |
695 | #]] |
8990e307 |
696 | # |
93a17b20 |
697 | EOF |
698 | } |
699 | |
700 | # print initialization routine |
8990e307 |
701 | print qq/extern "C"\n/ if $cplusplus; |
702 | print Q<<"EOF"; |
a0d0e21e |
703 | #XS(boot_$Module_cname) |
2304df62 |
704 | #[[ |
a0d0e21e |
705 | # dXSARGS; |
8990e307 |
706 | # char* file = __FILE__; |
707 | # |
93a17b20 |
708 | EOF |
709 | |
8e07c86e |
710 | print Q<<"EOF" if defined %XsubAliases ; |
711 | # { |
712 | # CV * cv ; |
713 | # |
714 | EOF |
715 | |
93a17b20 |
716 | for (@Func_name) { |
2304df62 |
717 | $pname = shift(@Func_pname); |
8e07c86e |
718 | |
719 | if ($XsubAliases{$pname}) { |
720 | $XsubAliases{$pname}{$pname} = 0 |
721 | unless defined $XsubAliases{$pname}{$pname} ; |
722 | while ( ($name, $value) = each %{$XsubAliases{$pname}}) { |
723 | print Q<<"EOF" ; |
724 | # cv = newXS(\"$name\", XS_$_, file); |
725 | # XSANY.any_i32 = $value ; |
726 | EOF |
727 | } |
728 | } |
729 | else { |
730 | print " newXS(\"$pname\", XS_$_, file);\n"; |
731 | } |
a0d0e21e |
732 | } |
733 | |
8e07c86e |
734 | print Q<<"EOF" if defined %XsubAliases ; |
735 | # } |
736 | EOF |
737 | |
a0d0e21e |
738 | if (@BootCode) |
739 | { |
8e07c86e |
740 | print "\n /* Initialisation Section */\n" ; |
a0d0e21e |
741 | print grep (s/$/\n/, @BootCode) ; |
8e07c86e |
742 | print "\n /* End of Initialisation Section */\n\n" ; |
93a17b20 |
743 | } |
a0d0e21e |
744 | |
e50aee73 |
745 | print Q<<"EOF";; |
746 | # ST(0) = &sv_yes; |
747 | # XSRETURN(1); |
748 | #]] |
749 | EOF |
750 | |
751 | &Exit; |
752 | |
93a17b20 |
753 | |
754 | sub output_init { |
2304df62 |
755 | local($type, $num, $init) = @_; |
a0d0e21e |
756 | local($arg) = "ST(" . ($num - 1) . ")"; |
93a17b20 |
757 | |
2304df62 |
758 | eval qq/print " $init\\\n"/; |
93a17b20 |
759 | } |
760 | |
c2960299 |
761 | sub Warn |
762 | { |
763 | # work out the line number |
764 | my $line_no = $line_no[@line_no - @line -1] ; |
765 | |
766 | print STDERR "@_ in $filename, line $line_no\n" ; |
767 | } |
768 | |
769 | sub blurt |
770 | { |
771 | Warn @_ ; |
772 | $errors ++ |
773 | } |
774 | |
775 | sub death |
776 | { |
777 | Warn @_ ; |
778 | exit 1 ; |
779 | } |
8990e307 |
780 | |
93a17b20 |
781 | sub generate_init { |
2304df62 |
782 | local($type, $num, $var) = @_; |
a0d0e21e |
783 | local($arg) = "ST(" . ($num - 1) . ")"; |
2304df62 |
784 | local($argoff) = $num - 1; |
785 | local($ntype); |
786 | local($tk); |
93a17b20 |
787 | |
f06db76b |
788 | $type = TidyType($type) ; |
c2960299 |
789 | blurt("Error: '$type' not in typemap"), return |
790 | unless defined($type_kind{$type}); |
791 | |
2304df62 |
792 | ($ntype = $type) =~ s/\s*\*/Ptr/g; |
8e07c86e |
793 | ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//; |
2304df62 |
794 | $tk = $type_kind{$type}; |
795 | $tk =~ s/OBJ$/REF/ if $func_name =~ /DESTROY$/; |
8e07c86e |
796 | $type =~ tr/:/_/; |
c2960299 |
797 | blurt("Error: No INPUT definition for type '$type' found"), return |
798 | unless defined $input_expr{$tk} ; |
2304df62 |
799 | $expr = $input_expr{$tk}; |
800 | if ($expr =~ /DO_ARRAY_ELEM/) { |
c2960299 |
801 | blurt("Error: '$subtype' not in typemap"), return |
802 | unless defined($type_kind{$subtype}); |
803 | blurt("Error: No INPUT definition for type '$subtype' found"), return |
804 | unless defined $input_expr{$type_kind{$subtype}} ; |
2304df62 |
805 | $subexpr = $input_expr{$type_kind{$subtype}}; |
806 | $subexpr =~ s/ntype/subtype/g; |
807 | $subexpr =~ s/\$arg/ST(ix_$var)/g; |
808 | $subexpr =~ s/\n\t/\n\t\t/g; |
809 | $subexpr =~ s/is not of (.*")/[arg %d] is not of $1, ix_$var + 1/g; |
a0d0e21e |
810 | $subexpr =~ s/\$var/${var}[ix_$var - $argoff]/; |
2304df62 |
811 | $expr =~ s/DO_ARRAY_ELEM/$subexpr/; |
812 | } |
813 | if (defined($defaults{$var})) { |
814 | $expr =~ s/(\t+)/$1 /g; |
815 | $expr =~ s/ /\t/g; |
816 | eval qq/print "\\t$var;\\n"/; |
817 | $deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/; |
818 | } elsif ($expr !~ /^\t\$var =/) { |
819 | eval qq/print "\\t$var;\\n"/; |
820 | $deferred .= eval qq/"\\n$expr;\\n"/; |
821 | } else { |
822 | eval qq/print "$expr;\\n"/; |
823 | } |
93a17b20 |
824 | } |
825 | |
826 | sub generate_output { |
2304df62 |
827 | local($type, $num, $var) = @_; |
a0d0e21e |
828 | local($arg) = "ST(" . ($num - ($num != 0)) . ")"; |
2304df62 |
829 | local($argoff) = $num - 1; |
830 | local($ntype); |
93a17b20 |
831 | |
f06db76b |
832 | $type = TidyType($type) ; |
2304df62 |
833 | if ($type =~ /^array\(([^,]*),(.*)\)/) { |
834 | print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1)), XFree((char *)$var);\n"; |
835 | } else { |
f06db76b |
836 | blurt("Error: '$type' not in typemap"), return |
2304df62 |
837 | unless defined($type_kind{$type}); |
c2960299 |
838 | blurt("Error: No OUTPUT definition for type '$type' found"), return |
839 | unless defined $output_expr{$type_kind{$type}} ; |
2304df62 |
840 | ($ntype = $type) =~ s/\s*\*/Ptr/g; |
841 | $ntype =~ s/\(\)//g; |
8e07c86e |
842 | ($subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//; |
2304df62 |
843 | $expr = $output_expr{$type_kind{$type}}; |
844 | if ($expr =~ /DO_ARRAY_ELEM/) { |
c2960299 |
845 | blurt("Error: '$subtype' not in typemap"), return |
846 | unless defined($type_kind{$subtype}); |
847 | blurt("Error: No OUTPUT definition for type '$subtype' found"), return |
848 | unless defined $output_expr{$type_kind{$subtype}} ; |
2304df62 |
849 | $subexpr = $output_expr{$type_kind{$subtype}}; |
850 | $subexpr =~ s/ntype/subtype/g; |
851 | $subexpr =~ s/\$arg/ST(ix_$var)/g; |
852 | $subexpr =~ s/\$var/${var}[ix_$var]/g; |
853 | $subexpr =~ s/\n\t/\n\t\t/g; |
854 | $expr =~ s/DO_ARRAY_ELEM\n/$subexpr/; |
a0d0e21e |
855 | eval "print qq\a$expr\a"; |
2304df62 |
856 | } |
a0d0e21e |
857 | elsif ($var eq 'RETVAL') { |
2304df62 |
858 | if ($expr =~ /^\t\$arg = /) { |
a0d0e21e |
859 | eval "print qq\a$expr\a"; |
2304df62 |
860 | print "\tsv_2mortal(ST(0));\n"; |
93a17b20 |
861 | } |
2304df62 |
862 | else { |
8990e307 |
863 | print "\tST(0) = sv_newmortal();\n"; |
a0d0e21e |
864 | eval "print qq\a$expr\a"; |
463ee0b2 |
865 | } |
2304df62 |
866 | } |
a0d0e21e |
867 | elsif ($arg =~ /^ST\(\d+\)$/) { |
868 | eval "print qq\a$expr\a"; |
869 | } |
2304df62 |
870 | } |
93a17b20 |
871 | } |
872 | |
873 | sub map_type { |
e50aee73 |
874 | my($type) = @_; |
93a17b20 |
875 | |
8e07c86e |
876 | $type =~ tr/:/_/; |
877 | $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s; |
878 | $type; |
93a17b20 |
879 | } |
8990e307 |
880 | |
e50aee73 |
881 | |
882 | sub Exit { |
748a9306 |
883 | # If this is VMS, the exit status has meaning to the shell, so we |
884 | # use a predictable value (SS$_Abort) rather than an arbitrary |
885 | # number. |
e50aee73 |
886 | exit ($Is_VMS ? 44 : $errors) ; |
887 | } |