1 package ExtUtils::XSpp::Node;
5 ExtUtils::XSpp::Node - Base class for the parser output.
14 my $this = bless {}, $class;
21 =head2 ExtUtils::XSpp::Node::print
23 Return a string to be output in the final XS file.
24 Every class must override this method.
28 package ExtUtils::XSpp::Node::Raw;
30 =head1 ExtUtils::XSpp::Node::Raw
32 Contains data that should be output "as is" in the destination file.
37 use base 'ExtUtils::XSpp::Node';
43 $this->{ROWS} = $args{rows};
44 push @{$this->{ROWS}}, "\n";
47 =head2 ExtUtils::XSpp::Node::Raw::rows
49 Returns an array reference holding the rows to be output in the final file.
53 sub rows { $_[0]->{ROWS} }
54 sub print { join( "\n", @{$_[0]->rows} ) . "\n" }
56 package ExtUtils::XSpp::Node::Comment;
58 =head1 ExtUtils::XSpp::Node::Comment
60 Contains data that should be output prefixed with a comment marker
65 use base 'ExtUtils::XSpp::Node::Raw';
71 $this->{ROWS} = $args{rows};
81 package ExtUtils::XSpp::Node::Package;
83 =head1 ExtUtils::XSpp::Node::Package
85 Used to put global functions inside a Perl package.
90 use base 'ExtUtils::XSpp::Node';
96 $this->{CPP_NAME} = $args{cpp_name};
97 $this->{PERL_NAME} = $args{perl_name} || $args{cpp_name};
100 =head2 ExtUtils::XSpp::Node::Package::cpp_name
102 Returns the C++ name for the package (will be used for namespaces).
104 =head2 ExtUtils::XSpp::Node::Package::perl_name
106 Returns the Perl name for the package.
110 sub cpp_name { $_[0]->{CPP_NAME} }
111 sub perl_name { $_[0]->{PERL_NAME} }
112 sub set_perl_name { $_[0]->{PERL_NAME} = $_[1] }
118 my $pcname = $this->perl_name;
120 if( !defined $state->{current_module} ) {
121 die "No current module: remember to add a %module{} directive";
123 my $cur_module = $state->{current_module}->to_string;
127 $cur_module PACKAGE=$pcname
134 package ExtUtils::XSpp::Node::Class;
136 =head1 ExtUtils::XSpp::Node::Class
138 A class (inherits from Package).
143 use base 'ExtUtils::XSpp::Node::Package';
149 $this->SUPER::init( @_ );
150 $this->{METHODS} = $args{methods} || [];
151 $this->{BASE_CLASSES} = $args{base_classes} || [];
154 =head2 ExtUtils::XSpp::Node::Class::methods
158 sub methods { $_[0]->{METHODS} }
159 sub base_classes { $_[0]->{BASE_CLASSES} }
163 my $access = 'public'; # good enough for now
164 foreach my $meth ( @_ ) {
165 if( $meth->isa( 'ExtUtils::XSpp::Node::Method' ) ) {
166 $meth->{CLASS} = $this;
167 $meth->{ACCESS} = $access;
168 $meth->resolve_typemaps;
169 } elsif( $meth->isa( 'ExtUtils::XSpp::Node::Access' ) ) {
170 $access = $meth->access;
173 push @{$this->{METHODS}}, $meth;
180 my $out = $this->SUPER::print( $state );
182 foreach my $m ( @{$this->methods} ) {
183 $out .= $m->print( $state );
186 # add a BOOT block for base classes
187 if( @{$this->base_classes} ) {
188 my $class = $this->perl_name;
193 AV* isa = get_av( "${class}::ISA", 1 );
196 foreach my $b ( @{$this->base_classes} ) {
197 my $base = $b->perl_name;
200 av_store( isa, 0, newSVpv( "$base", 0 ) );
204 # close block in BOOT
206 } // blank line here is important
214 package ExtUtils::XSpp::Node::Access;
216 =head1 ExtUtils::XSpp::Node::Access
223 use base 'ExtUtils::XSpp::Node';
229 $this->{ACCESS} = $args{access};
232 sub access { $_[0]->{ACCESS} }
234 package ExtUtils::XSpp::Node::Function;
237 use base 'ExtUtils::XSpp::Node';
239 =head1 ExtUtils::XSpp::Node::Function
241 A function; this is also a base class for C<Method>.
249 $this->{CPP_NAME} = $args{cpp_name};
250 $this->{PERL_NAME} = $args{perl_name} || $args{cpp_name};
251 $this->{ARGUMENTS} = $args{arguments} || [];
252 $this->{RET_TYPE} = $args{ret_type};
253 $this->{CODE} = $args{code};
254 $this->{CLEANUP} = $args{cleanup};
255 $this->{POSTCALL} = $args{postcall};
256 $this->{CLASS} = $args{class};
259 sub resolve_typemaps {
262 if( $this->ret_type ) {
263 $this->{TYPEMAPS}{RET_TYPE} =
264 ExtUtils::XSpp::Typemap::get_typemap_for_type( $this->ret_type );
266 foreach my $a ( @{$this->arguments} ) {
267 my $t = ExtUtils::XSpp::Typemap::get_typemap_for_type( $a->type );
268 push @{$this->{TYPEMAPS}{ARGUMENTS}}, $t;
272 =head2 ExtUtils::XSpp::Node::Function::cpp_name
274 =head2 ExtUtils::XSpp::Node::Function::perl_name
276 =head2 ExtUtils::XSpp::Node::Function::arguments
278 =head2 ExtUtils::XSpp::Node::Function::ret_type
280 =head2 ExtUtils::XSpp::Node::Function::code
282 =head2 ExtUtils::XSpp::Node::Function::cleanup
284 =head2 ExtUtils::XSpp::Node::Function::postcall
286 =head2 ExtUtils::XSpp::Node::Function::argument_style
288 Returns either C<ansi> or C<kr>. C<kr> is the default.
289 C<ansi> is returned if any one of the arguments uses the XS
294 sub cpp_name { $_[0]->{CPP_NAME} }
295 sub perl_name { $_[0]->{PERL_NAME} }
296 sub arguments { $_[0]->{ARGUMENTS} }
297 sub ret_type { $_[0]->{RET_TYPE} }
298 sub code { $_[0]->{CODE} }
299 sub cleanup { $_[0]->{CLEANUP} }
300 sub postcall { $_[0]->{POSTCALL} }
301 sub package_static { ( $_[0]->{STATIC} || '' ) eq 'package_static' }
302 sub class_static { ( $_[0]->{STATIC} || '' ) eq 'class_static' }
303 sub virtual { $_[0]->{VIRTUAL} }
305 sub set_perl_name { $_[0]->{PERL_NAME} = $_[1] }
306 sub set_static { $_[0]->{STATIC} = $_[1] }
307 sub set_virtual { $_[0]->{VIRTUAL} = $_[1] }
311 foreach my $arg (@{$this->{ARGUMENTS}}) {
312 return 'ansi' if $arg->name =~ /length.*\(/;
317 # Depending on argument style, this produces either: (style=kr)
320 # class_name::function_name( args = def, ... )
326 # RETVAL = new Foo( THIS->method( arg1, *arg2 ) );
337 # class_name::function_name( type arg1 = def, type arg2 = def, ... )
345 my $fname = $this->perl_function_name;
346 my $args = $this->arguments;
347 my $ret_type = $this->ret_type;
348 my $ret_typemap = $this->{TYPEMAPS}{RET_TYPE};
349 my $need_call_function = 0;
350 my( $init, $arg_list, $call_arg_list, $code, $output, $cleanup,
351 $postcall, $precall ) =
352 ( '', '', '', '', '', '', '', '' );
353 my $use_ansi_style = $this->argument_style() eq 'ansi';
355 if( $args && @$args ) {
356 my $has_self = $this->is_method ? 1 : 0;
357 my( @arg_list, @call_arg_list );
358 foreach my $i ( 0 .. $#$args ) {
359 my $arg = ${$args}[$i];
360 my $t = $this->{TYPEMAPS}{ARGUMENTS}[$i];
361 my $pc = $t->precall_code( sprintf( 'ST(%d)', $i + $has_self ),
364 $need_call_function ||= defined $t->call_parameter_code( '' )
366 my $type = $use_ansi_style ? $t->cpp_type . ' ' : '';
367 push @arg_list, $type . $arg->name . ( $arg->has_default ? ' = ' . $arg->default : '' );
368 if (!$use_ansi_style) {
369 $init .= ' ' . $t->cpp_type . ' ' . $arg->name . "\n";
372 my $call_code = $t->call_parameter_code( $arg->name );
373 push @call_arg_list, defined( $call_code ) ? $call_code : $arg->name;
374 $precall .= $pc . ";\n" if $pc
377 $arg_list = ' ' . join( ', ', @arg_list ) . ' ';
378 $call_arg_list = ' ' . join( ', ', @call_arg_list ) . ' ';
380 # same for return value
381 $need_call_function ||= $ret_typemap &&
382 ( defined $ret_typemap->call_function_code( '', '' ) ||
383 defined $ret_typemap->output_code ||
384 defined $ret_typemap->cleanup_code );
385 # is C++ name != Perl name?
386 $need_call_function ||= $this->cpp_name ne $this->perl_name;
387 # package-static function
388 $need_call_function ||= $this->package_static;
390 my $retstr = $ret_typemap ? $ret_typemap->cpp_type : 'void';
392 # special case: constructors with name different from 'new'
393 # need to be declared 'static' in XS
394 if( $this->isa( 'ExtUtils::XSpp::Node::Constructor' ) &&
395 $this->perl_name ne $this->cpp_name ) {
396 $retstr = "static $retstr";
399 my $has_ret = $ret_typemap && !$ret_typemap->type->is_void;
400 if( $need_call_function ) {
401 my $ccode = $this->_call_code( $call_arg_list );
402 if( $has_ret && defined $ret_typemap->call_function_code( '', '' ) ) {
403 $ccode = $ret_typemap->call_function_code( $ccode, 'RETVAL' );
404 } elsif( $has_ret ) {
405 $ccode = "RETVAL = $ccode";
409 $code .= ' ' . $precall if $precall;
410 $code .= ' ' . $ccode . ";\n";
412 if( $has_ret && defined $ret_typemap->output_code ) {
413 $code .= ' ' . $ret_typemap->output_code . ";\n";
415 $output = " OUTPUT: RETVAL\n" if $has_ret;
417 if( $has_ret && defined $ret_typemap->cleanup_code ) {
418 $cleanup .= " CLEANUP:\n";
419 $cleanup .= ' ' . $ret_typemap->cleanup_code . ";\n";
424 $code = " CODE:\n " . join( "\n", @{$this->code} ) . "\n";
425 $output = " OUTPUT: RETVAL\n" if $code =~ m/RETVAL/;
427 if( $this->postcall ) {
428 $postcall = " POSTCALL:\n " . join( "\n", @{$this->postcall} ) . "\n";
429 $output ||= " OUTPUT: RETVAL\n" if $has_ret;
431 if( $this->cleanup ) {
432 $cleanup ||= " CLEANUP:\n";
433 my $clcode = join( "\n", @{$this->cleanup} );
434 $cleanup .= " $clcode\n";
437 if( !$this->is_method && $fname =~ /^(.*)::(\w+)$/ ) {
440 my $cur_module = $state->{current_module}->to_string;
442 $cur_module PACKAGE=$pcname
448 $out .= "$fname($arg_list)\n";
457 sub perl_function_name { $_[0]->perl_name }
462 ExtUtils::XSpp::Node::_call_code( argument_string )
464 Return something like "foo( $argument_string )".
470 sub _call_code { return $_[0]->cpp_name . '(' . $_[1] . ')'; }
472 package ExtUtils::XSpp::Node::Method;
475 use base 'ExtUtils::XSpp::Node::Function';
477 sub class { $_[0]->{CLASS} }
478 sub perl_function_name { $_[0]->class->cpp_name . '::' .
483 if( $self->package_static ) {
484 return $_[0]->class->cpp_name . '::' .
485 $_[0]->cpp_name . '(' . $_[1] . ')';
488 $_[0]->cpp_name . '(' . $_[1] . ')';
494 package ExtUtils::XSpp::Node::Constructor;
497 use base 'ExtUtils::XSpp::Node::Method';
501 $this->SUPER::init( @_ );
503 die "Can't specify return value in constructor" if $this->{RET_TYPE};
509 ExtUtils::XSpp::Node::Type->new( base => $this->class->cpp_name,
513 sub perl_function_name {
515 my( $pname, $cname, $pclass, $cclass ) = ( $this->perl_name,
517 $this->class->perl_name,
518 $this->class->cpp_name );
520 if( $pname ne $cname ) {
521 return $cclass . '::' . $pname;
523 return $cclass . '::' . 'new';
527 sub _call_code { return "new " . $_[0]->class->cpp_name .
530 package ExtUtils::XSpp::Node::Destructor;
533 use base 'ExtUtils::XSpp::Node::Method';
537 $this->SUPER::init( @_ );
539 die "Can't specify return value in destructor" if $this->{RET_TYPE};
542 sub perl_function_name { $_[0]->class->cpp_name . '::' . 'DESTROY' }
543 sub ret_type { undef }
545 package ExtUtils::XSpp::Node::Argument;
548 use base 'ExtUtils::XSpp::Node';
554 $this->{TYPE} = $args{type};
555 $this->{NAME} = $args{name};
556 $this->{DEFAULT} = $args{default};
564 $this->type->print( $state ),
567 ( '=', $this->default ) : () ) );
570 sub type { $_[0]->{TYPE} }
571 sub name { $_[0]->{NAME} }
572 sub default { $_[0]->{DEFAULT} }
573 sub has_default { defined $_[0]->{DEFAULT} }
575 package ExtUtils::XSpp::Node::Type;
578 use base 'ExtUtils::XSpp::Node';
580 # normalized names for some integral C types
582 ( 'unsigned' => 'unsigned int',
583 'long int' => 'long',
584 'unsigned long int' => 'unsigned long',
585 'short int' => 'short',
586 'unsigned short int' => 'unsigned short',
593 $this->{BASE} = $normalize{$args{base}} || $args{base};
594 $this->{POINTER} = $args{pointer} ? 1 : 0;
595 $this->{REFERENCE} = $args{reference} ? 1 : 0;
596 $this->{CONST} = $args{const} ? 1 : 0;
597 $this->{TEMPLATE_ARGS} = $args{template_args} || [];
600 sub is_const { $_[0]->{CONST} }
601 sub is_reference { $_[0]->{REFERENCE} }
602 sub is_pointer { $_[0]->{POINTER} }
603 sub base_type { $_[0]->{BASE} }
604 sub template_args { $_[0]->{TEMPLATE_ARGS} }
609 return 0 if @{$f->template_args} != @{$s->template_args};
611 for( my $i = 0; $i < @{$f->template_args}; ++$i ) {
613 unless $f->template_args->[$i]->equals( $s->template_args->[$i] );
616 return $f->is_const == $s->is_const
617 && $f->is_reference == $s->is_reference
618 && $f->is_pointer == $s->is_pointer
619 && $f->base_type eq $s->base_type;
622 sub is_void { return $_[0]->base_type eq 'void' &&
623 !$_[0]->is_pointer && !$_[0]->is_reference }
625 sub print_tmpl_args {
629 if( @{$this->template_args} ) {
632 map $_->print( $state ), @{$this->template_args} )
643 ( $this->is_const ? 'const ' : '' ),
645 $this->print_tmpl_args,
646 ( $this->is_pointer ? ( '*' x $this->is_pointer ) :
647 $this->is_reference ? '&' : '' ) );
651 package ExtUtils::XSpp::Node::Module;
654 use base 'ExtUtils::XSpp::Node';
660 $this->{MODULE} = $args{module};
663 sub module { $_[0]->{MODULE} }
664 sub to_string { 'MODULE=' . $_[0]->module }
665 sub print { return $_[0]->to_string . "\n" }
667 package ExtUtils::XSpp::Node::File;
670 use base 'ExtUtils::XSpp::Node';
676 $this->{FILE} = $args{file};
679 sub file { $_[0]->{FILE} }