summaryrefslogtreecommitdiff
path: root/lib/ExtUtils/xsubpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ExtUtils/xsubpp')
-rwxr-xr-xlib/ExtUtils/xsubpp616
1 files changed, 616 insertions, 0 deletions
diff --git a/lib/ExtUtils/xsubpp b/lib/ExtUtils/xsubpp
new file mode 100755
index 0000000000..1e13118ad5
--- /dev/null
+++ b/lib/ExtUtils/xsubpp
@@ -0,0 +1,616 @@
+#!./miniperl
+'di ';
+'ds 00 \"';
+'ig 00 ';
+# $Header$
+
+$usage = "Usage: xsubpp [-C++] [-except] [-typemap typemap] file.xs\n";
+
+SWITCH: while ($ARGV[0] =~ s/^-//) {
+ $flag = shift @ARGV;
+ $spat = shift, next SWITCH if $flag eq 's';
+ $cplusplus = 1, next SWITCH if $flag eq 'C++';
+ $except = 1, next SWITCH if $flag eq 'except';
+ push(@tm,shift), next SWITCH if $flag eq 'typemap';
+ die $usage;
+}
+@ARGV == 1 or die $usage;
+chop($pwd = `pwd`);
+($dir, $filename) = @ARGV[0] =~ m#(.*)/(.*)#
+ or ($dir, $filename) = @ARGV[0] =~ m#(.*[>\]])(.*)#
+ or ($dir, $filename) = ('.', $ARGV[0]);
+chdir($dir);
+
+$typemap = shift @ARGV;
+foreach $typemap (@tm) {
+ die "Can't find $typemap in $pwd\n" unless -r $typemap;
+}
+unshift @tm, qw(../../../typemap ../../typemap ../typemap typemap);
+foreach $typemap (@tm) {
+ open(TYPEMAP, $typemap) || next;
+ $mode = Typemap;
+ $current = \$junk;
+ while (<TYPEMAP>) {
+ next if /^#/;
+ if (/^INPUT\s*$/) { $mode = Input, next }
+ if (/^OUTPUT\s*$/) { $mode = Output, next }
+ if (/^TYPEMAP\s*$/) { $mode = Typemap, next }
+ if ($mode eq Typemap) {
+ chop;
+ ($typename, $kind) = split(/\t+/, $_, 2);
+ $type_kind{$typename} = $kind if $kind ne '';
+ }
+ elsif ($mode eq Input) {
+ if (/^\s/) {
+ $$current .= $_;
+ }
+ else {
+ s/\s*$//;
+ $input_expr{$_} = '';
+ $current = \$input_expr{$_};
+ }
+ }
+ else {
+ if (/^\s/) {
+ $$current .= $_;
+ }
+ else {
+ s/\s*$//;
+ $output_expr{$_} = '';
+ $current = \$output_expr{$_};
+ }
+ }
+ }
+ close(TYPEMAP);
+}
+
+foreach $key (keys %input_expr) {
+ $input_expr{$key} =~ s/\n+$//;
+}
+
+sub Q {
+ local $text = shift;
+ $text =~ tr/#//d;
+ $text =~ s/\[\[/{/g;
+ $text =~ s/\]\]/}/g;
+ $text;
+}
+
+open(F, $filename) || die "cannot open $filename\n";
+
+while (<F>) {
+ last if ($Module, $foo, $Package, $foo1, $Prefix) =
+ /^MODULE\s*=\s*([\w:]+)(\s+PACKAGE\s*=\s*([\w:]+))?(\s+PREFIX\s*=\s*(\S+))?\s*$/;
+ print $_;
+}
+exit 0 if $_ eq "";
+$lastline = $_;
+
+sub fetch_para {
+ # parse paragraph
+ @line = ();
+ if ($lastline ne "") {
+ if ($lastline =~
+ /^MODULE\s*=\s*([\w:]+)(\s+PACKAGE\s*=\s*([\w:]+))?(\s+PREFIX\s*=\s*(\S+))?\s*$/) {
+ $Module = $1;
+ $foo = $2;
+ $Package = $3;
+ $foo1 = $4;
+ $Prefix = $5;
+ ($Module_cname = $Module) =~ s/\W/_/g;
+ ($Packid = $Package) =~ s/:/_/g;
+ $Packprefix = $Package;
+ $Packprefix .= "::" if defined $Packprefix && $Packprefix ne "";
+ while (<F>) {
+ chop;
+ next if /^#/ &&
+ !/^#[ \t]*(if|ifdef|ifndef|else|elif|endif|define|undef)\b/;
+ last if /^\S/;
+ }
+ push(@line, $_) if $_ ne "";
+ }
+ else {
+ push(@line, $lastline);
+ }
+ $lastline = "";
+ while (<F>) {
+ next if /^#/ &&
+ !/^#[ \t]*(if|ifdef|ifndef|else|elif|endif|define|undef)\b/;
+ chop;
+ if (/^\S/ && @line && $line[-1] eq "") {
+ $lastline = $_;
+ last;
+ }
+ else {
+ push(@line, $_);
+ }
+ }
+ pop(@line) while @line && $line[-1] =~ /^\s*$/;
+ }
+ $PPCODE = grep(/PPCODE:/, @line);
+ scalar @line;
+}
+
+while (&fetch_para) {
+ # initialize info arrays
+ undef(%args_match);
+ undef(%var_types);
+ undef(%var_addr);
+ undef(%defaults);
+ undef($class);
+ undef($static);
+ undef($elipsis);
+
+ # extract return type, function name and arguments
+ $ret_type = shift(@line);
+ if ($ret_type =~ /^BOOT:/) {
+ push (@BootCode, @line, "", "") ;
+ next ;
+ }
+ if ($ret_type =~ /^static\s+(.*)$/) {
+ $static = 1;
+ $ret_type = $1;
+ }
+ $func_header = shift(@line);
+ ($func_name, $orig_args) = $func_header =~ /^([\w:]+)\s*\((.*)\)$/;
+ if ($func_name =~ /(.*)::(.*)/) {
+ $class = $1;
+ $func_name = $2;
+ }
+ ($pname = $func_name) =~ s/^($Prefix)?/$Packprefix/;
+ push(@Func_name, "${Packid}_$func_name");
+ push(@Func_pname, $pname);
+ @args = split(/\s*,\s*/, $orig_args);
+ if (defined($class)) {
+ if (defined($static)) {
+ unshift(@args, "CLASS");
+ $orig_args = "CLASS, $orig_args";
+ $orig_args =~ s/^CLASS, $/CLASS/;
+ }
+ else {
+ unshift(@args, "THIS");
+ $orig_args = "THIS, $orig_args";
+ $orig_args =~ s/^THIS, $/THIS/;
+ }
+ }
+ $orig_args =~ s/"/\\"/g;
+ $min_args = $num_args = @args;
+ foreach $i (0..$num_args-1) {
+ if ($args[$i] =~ s/\.\.\.//) {
+ $elipsis = 1;
+ $min_args--;
+ if ($args[i] eq '' && $i == $num_args - 1) {
+ pop(@args);
+ last;
+ }
+ }
+ if ($args[$i] =~ /([^=]*\S)\s*=\s*(.*)/) {
+ $min_args--;
+ $args[$i] = $1;
+ $defaults{$args[$i]} = $2;
+ $defaults{$args[$i]} =~ s/"/\\"/g;
+ }
+ }
+ if (defined($class)) {
+ $func_args = join(", ", @args[1..$#args]);
+ } else {
+ $func_args = join(", ", @args);
+ }
+ @args_match{@args} = 1..@args;
+
+ # print function header
+ print Q<<"EOF";
+#XS(XS_${Packid}_$func_name)
+#[[
+# dXSARGS;
+EOF
+ if ($elipsis) {
+ $cond = qq(items < $min_args);
+ }
+ elsif ($min_args == $num_args) {
+ $cond = qq(items != $min_args);
+ }
+ else {
+ $cond = qq(items < $min_args || items > $num_args);
+ }
+
+ print Q<<"EOF" if $except;
+# char errbuf[1024];
+# *errbuf = '\0';
+EOF
+
+ print Q<<"EOF";
+# if ($cond) {
+# croak("Usage: $pname($orig_args)");
+# }
+EOF
+
+ print Q<<"EOF" if $PPCODE;
+# SP -= items;
+EOF
+
+ # Now do a block of some sort.
+
+ $condnum = 0;
+ if (!@line) {
+ @line = "CLEANUP:";
+ }
+ while (@line) {
+ if ($_[0] =~ s/^\s*CASE\s*:\s*//) {
+ $cond = shift(@line);
+ if ($condnum == 0) {
+ print " if ($cond)\n";
+ }
+ elsif ($cond ne '') {
+ print " else if ($cond)\n";
+ }
+ else {
+ print " else\n";
+ }
+ $condnum++;
+ }
+
+ if ($except) {
+ print Q<<"EOF";
+# TRY [[
+EOF
+ }
+ else {
+ print Q<<"EOF";
+# [[
+EOF
+ }
+
+ # do initialization of input variables
+ $thisdone = 0;
+ $retvaldone = 0;
+ $deferred = "";
+ while (@line) {
+ $_ = shift(@line);
+ last if /^\s*NOT_IMPLEMENTED_YET/;
+ last if /^\s*(PPCODE|CODE|OUTPUT|CLEANUP|CASE)\s*:/;
+ # Catch common error. Much more error checking required here.
+ blurt("Error: no tab in $pname argument declaration '$_'\n")
+ unless (m/\S+\s*\t\s*\S+/);
+ ($var_type, $var_name, $var_init) =
+ /\s*([^\t]+)\s*([^\s=]+)\s*(=.*)?/;
+ if ($var_name =~ /^&/) {
+ $var_name =~ s/^&//;
+ $var_addr{$var_name} = 1;
+ }
+ $thisdone |= $var_name eq "THIS";
+ $retvaldone |= $var_name eq "RETVAL";
+ $var_types{$var_name} = $var_type;
+ print "\t" . &map_type($var_type);
+ $var_num = $args_match{$var_name};
+ if ($var_addr{$var_name}) {
+ $func_args =~ s/\b($var_name)\b/&\1/;
+ }
+ if ($var_init !~ /^=\s*NO_INIT\s*$/) {
+ if ($var_init !~ /^\s*$/) {
+ &output_init($var_type, $var_num,
+ "$var_name $var_init");
+ } elsif ($var_num) {
+ # generate initialization code
+ &generate_init($var_type, $var_num, $var_name);
+ } else {
+ print ";\n";
+ }
+ } else {
+ print "\t$var_name;\n";
+ }
+ }
+ if (!$thisdone && defined($class)) {
+ if (defined($static)) {
+ print "\tchar *";
+ $var_types{"CLASS"} = "char *";
+ &generate_init("char *", 1, "CLASS");
+ }
+ else {
+ print "\t$class *";
+ $var_types{"THIS"} = "$class *";
+ &generate_init("$class *", 1, "THIS");
+ }
+ }
+
+ # do code
+ if (/^\s*NOT_IMPLEMENTED_YET/) {
+ print "\ncroak(\"$pname: not implemented yet\");\n";
+ } else {
+ if ($ret_type ne "void") {
+ print "\t" . &map_type($ret_type) . "\tRETVAL;\n"
+ if !$retvaldone;
+ $args_match{"RETVAL"} = 0;
+ $var_types{"RETVAL"} = $ret_type;
+ }
+ if (/^\s*PPCODE:/) {
+ print $deferred;
+ while (@line) {
+ $_ = shift(@line);
+ die "PPCODE must be last thing"
+ if /^\s*(OUTPUT|CLEANUP|CASE)\s*:/;
+ print "$_\n";
+ }
+ print "\tPUTBACK;\n\treturn;\n";
+ } elsif (/^\s*CODE:/) {
+ print $deferred;
+ while (@line) {
+ $_ = shift(@line);
+ last if /^\s*(OUTPUT|CLEANUP|CASE)\s*:/;
+ print "$_\n";
+ }
+ } elsif ($func_name eq "DESTROY") {
+ print $deferred;
+ print "\n\t";
+ print "delete THIS;\n"
+ } else {
+ print $deferred;
+ print "\n\t";
+ if ($ret_type ne "void") {
+ print "RETVAL = ";
+ }
+ if (defined($static)) {
+ if ($func_name =~ /^new/) {
+ $func_name = "$class";
+ }
+ else {
+ print "$class::";
+ }
+ } elsif (defined($class)) {
+ print "THIS->";
+ }
+ if (defined($spat) && $func_name =~ /^($spat)(.*)$/) {
+ $func_name = $2;
+ }
+ print "$func_name($func_args);\n";
+ &generate_output($ret_type, 0, "RETVAL")
+ unless $ret_type eq "void";
+ }
+ }
+
+ # do output variables
+ if (/^\s*OUTPUT\s*:/) {
+ while (@line) {
+ $_ = shift(@line);
+ last if /^\s*CLEANUP\s*:/;
+ s/^\s+//;
+ ($outarg, $outcode) = split(/\t+/);
+ if ($outcode) {
+ print "\t$outcode\n";
+ } else {
+ die "$outarg not an argument"
+ unless defined($args_match{$outarg});
+ $var_num = $args_match{$outarg};
+ &generate_output($var_types{$outarg}, $var_num,
+ $outarg);
+ }
+ }
+ }
+ # do cleanup
+ if (/^\s*CLEANUP\s*:/) {
+ while (@line) {
+ $_ = shift(@line);
+ last if /^\s*CASE\s*:/;
+ print "$_\n";
+ }
+ }
+ # print function trailer
+ if ($except) {
+ print Q<<EOF;
+# ]]
+# BEGHANDLERS
+# CATCHALL
+# sprintf(errbuf, "%s: %s\\tpropagated", Xname, Xreason);
+# ENDHANDLERS
+EOF
+ }
+ else {
+ print Q<<EOF;
+# ]]
+EOF
+ }
+ if (/^\s*CASE\s*:/) {
+ unshift(@line, $_);
+ }
+ }
+
+ print Q<<EOF if $except;
+# if (errbuf[0])
+# croak(errbuf);
+EOF
+
+ print Q<<EOF unless $PPCODE;
+# XSRETURN(1);
+EOF
+
+ print Q<<EOF;
+#]]
+#
+EOF
+}
+
+# print initialization routine
+print qq/extern "C"\n/ if $cplusplus;
+print Q<<"EOF";
+#XS(boot_$Module_cname)
+#[[
+# dXSARGS;
+# char* file = __FILE__;
+#
+EOF
+
+for (@Func_name) {
+ $pname = shift(@Func_pname);
+ print " newXS(\"$pname\", XS_$_, file);\n";
+}
+
+if (@BootCode)
+{
+ print "\n /* Initialisation Section */\n\n" ;
+ print grep (s/$/\n/, @BootCode) ;
+ print " /* End of Initialisation Section */\n\n" ;
+}
+
+print " ST(0) = &sv_yes;\n";
+print " XSRETURN(1);\n";
+print "}\n";
+
+sub output_init {
+ local($type, $num, $init) = @_;
+ local($arg) = "ST(" . ($num - 1) . ")";
+
+ eval qq/print " $init\\\n"/;
+}
+
+sub blurt { warn @_; $errors++ }
+
+sub generate_init {
+ local($type, $num, $var) = @_;
+ local($arg) = "ST(" . ($num - 1) . ")";
+ local($argoff) = $num - 1;
+ local($ntype);
+ local($tk);
+
+ blurt("$type not in typemap"), return unless defined($type_kind{$type});
+ ($ntype = $type) =~ s/\s*\*/Ptr/g;
+ $subtype = $ntype;
+ $subtype =~ s/Ptr$//;
+ $subtype =~ s/Array$//;
+ $tk = $type_kind{$type};
+ $tk =~ s/OBJ$/REF/ if $func_name =~ /DESTROY$/;
+ $type =~ s/:/_/g;
+ $expr = $input_expr{$tk};
+ if ($expr =~ /DO_ARRAY_ELEM/) {
+ $subexpr = $input_expr{$type_kind{$subtype}};
+ $subexpr =~ s/ntype/subtype/g;
+ $subexpr =~ s/\$arg/ST(ix_$var)/g;
+ $subexpr =~ s/\n\t/\n\t\t/g;
+ $subexpr =~ s/is not of (.*")/[arg %d] is not of $1, ix_$var + 1/g;
+ $subexpr =~ s/\$var/${var}[ix_$var - $argoff]/;
+ $expr =~ s/DO_ARRAY_ELEM/$subexpr/;
+ }
+ if (defined($defaults{$var})) {
+ $expr =~ s/(\t+)/$1 /g;
+ $expr =~ s/ /\t/g;
+ eval qq/print "\\t$var;\\n"/;
+ $deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/;
+ } elsif ($expr !~ /^\t\$var =/) {
+ eval qq/print "\\t$var;\\n"/;
+ $deferred .= eval qq/"\\n$expr;\\n"/;
+ } else {
+ eval qq/print "$expr;\\n"/;
+ }
+}
+
+sub generate_output {
+ local($type, $num, $var) = @_;
+ local($arg) = "ST(" . ($num - ($num != 0)) . ")";
+ local($argoff) = $num - 1;
+ local($ntype);
+
+ if ($type =~ /^array\(([^,]*),(.*)\)/) {
+ print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1)), XFree((char *)$var);\n";
+ } else {
+ blurt("$type not in typemap"), return
+ unless defined($type_kind{$type});
+ ($ntype = $type) =~ s/\s*\*/Ptr/g;
+ $ntype =~ s/\(\)//g;
+ $subtype = $ntype;
+ $subtype =~ s/Ptr$//;
+ $subtype =~ s/Array$//;
+ $expr = $output_expr{$type_kind{$type}};
+ if ($expr =~ /DO_ARRAY_ELEM/) {
+ $subexpr = $output_expr{$type_kind{$subtype}};
+ $subexpr =~ s/ntype/subtype/g;
+ $subexpr =~ s/\$arg/ST(ix_$var)/g;
+ $subexpr =~ s/\$var/${var}[ix_$var]/g;
+ $subexpr =~ s/\n\t/\n\t\t/g;
+ $expr =~ s/DO_ARRAY_ELEM\n/$subexpr/;
+ eval "print qq\a$expr\a";
+ }
+ elsif ($var eq 'RETVAL') {
+ if ($expr =~ /^\t\$arg = /) {
+ eval "print qq\a$expr\a";
+ print "\tsv_2mortal(ST(0));\n";
+ }
+ else {
+ print "\tST(0) = sv_newmortal();\n";
+ eval "print qq\a$expr\a";
+ }
+ }
+ elsif ($arg =~ /^ST\(\d+\)$/) {
+ eval "print qq\a$expr\a";
+ }
+ elsif ($arg =~ /^ST\(\d+\)$/) {
+ eval "print qq\a$expr\a";
+ }
+ elsif ($arg =~ /^ST\(\d+\)$/) {
+ eval "print qq\a$expr\a";
+ }
+ }
+}
+
+sub map_type {
+ local($type) = @_;
+
+ $type =~ s/:/_/g;
+ if ($type =~ /^array\(([^,]*),(.*)\)/) {
+ return "$1 *";
+ } else {
+ return $type;
+ }
+}
+
+exit $errors;
+
+##############################################################################
+
+ # These next few lines are legal in both Perl and nroff.
+
+.00 ; # finish .ig
+
+'di \" finish diversion--previous line must be blank
+.nr nl 0-1 \" fake up transition to first page again
+.nr % 0 \" start at page 1
+'; __END__ ############# From here on it's a standard manual page ############
+.TH XSUBPP 1 "August 9, 1994"
+.AT 3
+.SH NAME
+xsubpp \- compiler to convert Perl XS code into C code
+.SH SYNOPSIS
+.B xsubpp [-C++] [-except] [-typemap typemap] file.xs
+.SH DESCRIPTION
+.I xsubpp
+will compile XS code into C code by embedding the constructs necessary to
+let C functions manipulate Perl values and creates the glue necessary to let
+Perl access those functions. The compiler uses typemaps to determine how
+to map C function parameters and variables to Perl values.
+.PP
+The compiler will search for typemap files called
+.I typemap.
+It will use the following search path to find default typemaps, with the
+rightmost typemap taking precedence.
+.br
+.nf
+ ../../../typemap:../../typemap:../typemap:typemap
+.fi
+.SH OPTIONS
+.TP
+.B \-C++
+.br
+Adds ``extern "C"'' to the C code.
+.TP
+.B \-except
+Adds exception handling stubs to the C code.
+.TP
+.B \-typemap typemap
+Indicates that a user-supplied typemap should take precedence over the
+default typemaps. This option may be used multiple times, with the last
+typemap having the highest precedence.
+.SH ENVIRONMENT
+No environment variables are used.
+.SH AUTHOR
+Larry Wall
+.SH "SEE ALSO"
+perl(1)
+.ex