trick/libexec/trick/convert_swig
2016-10-06 13:54:06 -05:00

771 lines
27 KiB
Perl
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/perl
use FindBin qw($RealBin);
use strict ;
use Getopt::Long;
use Pod::Usage;
use Pod::Text;
use Text::Balanced qw ( extract_bracketed );
use lib "$RealBin/pm" ;
use File::Basename ;
use Cwd 'abs_path' ;
use gte ;
use trick_version ;
use Digest::MD5 qw(md5_hex) ;
use File::Path qw(make_path) ;
##
## ================================================================================
## Program: convert_swig
##
## The purpose of convert_swig is to create SWIG interface files for the given
## C/C++ header file (usually S_source.hh) and each of the header files that it
## (recursively) includes. SWIG (Simplified Wrapper and Interface Generator) is
## an interface compiler that connects programs written in C and C++ with scripting
## languagues such as Perl and Python.
##
my $usage="
Name:
convert_swig - Convert a C/C++ header file into a SWIG interface file.
SWIG (Simplified Wrapper and Interface Generator) is an
interface compiler that connects programs written in C
and C++ with scripting languagues such as Perl and Python.
Options:
-h, --help
display this help and exit
-s, --stl
Allow convert_swig to process STLs
Usage:
convert_swig <IN_FILES>
Examples:
% convert_swig include/Ball.hh
% convert_swig S_source.hh
" ;
my $help = ''; # option variable with default value (false)
my $stls = 0; # option variable with default value (false)
my ( @include_dirs , @defines) ;
my ( @swig_exclude_dirs) ;
my ( @exclude_dirs) ;
my %sim ;
my %out_of_date ;
my ($version, $thread, $year) ;
my %processed_templates ;
my $typedef_def = qr/typedef\s+ # the word typedef
(?:[_A-Za-z][\s\w]*\s*) # resolved type
(?:[_A-Za-z]\w*) # new type
\s*; # semicolon
/sx ;
my $typedef_enum_def = qr/typedef\s+enum\s* # the words typedef enum
(?:\s+[_A-Za-z]\w*)?\s* # optional name
{(?:\s*\d+\s*)* # opening brace and possible comments
(?:.*?}\s* # everything to closing brace
[\w,\s\*]*\s*;) # enum name and ;
/sx ;
my $typedef_const_struct = qr/typedef\s+const\s+(?:struct|union)\s* # the words typedef const struct|union
(?:\s+[_A-Za-z]\w*)?\s* # optional name
{ # opening brace
/sx ;
my $typedef_struct = qr/typedef\s+(?:struct|union)\s* # the words typedef struct|union
(?:\s+[_A-Za-z]\w*)?\s* # optional name
{ # opening brace
/sx ;
my $namespace_def = qr/namespace\s* # keyword namespace
(?:\s+[_A-Za-z]\w*) # class name
/sx ;
my $class_def = qr/(?:class|struct)\s* # keyword class or struct
(?:\s+[_A-Za-z]\w*)\s* # class name
(?:\{|:(?!\:)) # { or punctuator :
/sx ;
my $template_def = qr/template\s* # keyword template
<[^>]+>\s* # template parameters
class\s*[_A-Za-z]\w*\s* # keyword class and class name
/sx ;
my $template_var_def = qr/(?:\:\:)?[_A-Za-z][:\w]*\s* # template name
<[\w\s\*,:<>]*>\s* # template parameters
[_A-Za-z]\w*\s*; # var name ;
/sx ;
# This list is the list of all STL types to ignore.
my %all_stl_names = qw(vector 1 list 1 deque 1 set 1 multiset 1 map 1 multimap 1 hash_set 1
hash_multiset 1 hash_map 1 hash_multimap 1 stack 1 queue 1 priority_queue 1 bitset 1 auto_ptr 1
std::vector 1 std::list 1 std::deque 1 std::set 1 std::multiset 1 std::map 1 std::multimap 1 std::hash_set 1
std::hash_multiset 1 std::hash_map 1 std::hash_multimap 1 std::stack 1 std::queue 1 std::priority_queue 1
std::bitset 1 std::auto_ptr 1 pair 1 std::pair 1 std::tr1::shared_ptr 1) ;
# This is a partial list of STL types to ignore. We do not ignore vector, map, list if we allow STLs
my %stl_names = qw(deque 1 set 1 multiset 1 multimap 1 hash_set 1
hash_multiset 1 hash_map 1 hash_multimap 1 stack 1 queue 1 priority_queue 1 bitset 1 auto_ptr 1
std::deque 1 std::set 1 std::multiset 1 std::multimap 1 std::hash_set 1
std::hash_multiset 1 std::hash_map 1 std::hash_multimap 1 std::stack 1 std::queue 1 std::priority_queue 1
std::bitset 1 std::auto_ptr 1 pair 1 std::pair 1 std::tr1::shared_ptr 1) ;
Getopt::Long::Configure ("bundling");
GetOptions ( "stl|s" => sub { $stls = 1 } ,
'help|h' => \$help ) or usage() ;
if ( $help ) {
usage() ;
}
($version, $thread) = get_trick_version() ;
($year) = $version =~ /^(\d+)/ ;
@include_dirs = $ENV{"TRICK_CFLAGS"} =~ /-I\s*(\S+)/g ; # get include paths from TRICK_CFLAGS
@swig_exclude_dirs = split /:/ , $ENV{"TRICK_SWIG_EXCLUDE"} ;
if ( scalar @swig_exclude_dirs == 0 ) {
@swig_exclude_dirs = split /:/ , $ENV{"TRICK_ICG_EXCLUDE"} ;
}
@exclude_dirs = split /:/ , $ENV{"TRICK_EXCLUDE"} ;
@defines = $ENV{"TRICK_CFLAGS"} =~ /(-D\S+)/g ; # get defines from TRICK_CFLAGS
if ( $ENV{"TRICK_CFLAGS"} !~ /DTRICK_VER=/ ) {
push @defines , "-DTRICK_VER=$year" ;
}
if ( scalar @ARGV == 0 ) {
my ($s_library_swig) = "build/S_library_swig" ;
open FILE_LIST, $s_library_swig or die "Could not open $s_library_swig" ;
while ( <FILE_LIST> ) {
chomp ;
my ($f) = $_ ;
my ($swig_dir , $base_file , $swig_file ) ;
$base_file = basename($f) ;
$base_file =~ s/\.[^\.]+$// ;
$swig_dir = dirname($f) ;
$swig_dir = "build/$swig_dir" ;
$swig_file = "$swig_dir/${base_file}.i" ;
#print "$swig_file\n" ;
if ( -e $swig_file ) {
if ( !exists $sim{mod_date}{$f} ) {
$sim{mod_date}{$f} = (stat $f)[9] ;
}
$sim{swig_mod_date}{$f} = (stat $swig_file)[9] ;
if ( $sim{mod_date}{$f} > $sim{swig_mod_date}{$f} ) {
$out_of_date{$f} = 1 ;
}
} else {
$out_of_date{$f} = 1 ;
$sim{swig_mod_date}{$f} = 0 ;
}
}
} else {
#print "HERE I AM @ARGV\n" ;
map { $out_of_date{$_} = 1 } @ARGV ;
}
## The global hash %out_of_date represents a list of header files whose
## corresponding .i files need to be regenerated.
##
if ( scalar keys %out_of_date == 0 ) {
exit ;
}
## Finally, call process_file() to create SWIG interface files for each of the out_of_date headers.
##
process_file() ;
##
## ================================================================================
## process_file
##
## Synopsis
##
## This subroutine processes S_source.h and each of it's requisite header files to
## generate the corresponding SWIG interfaces files.
##
## Parameters
##
## sim_ref
##
## The name of input file, invariably "S_source.hh".
##
sub process_file() {
## Foreach out_of_date header file, generate a SWIG interface file.
foreach my $f ( keys %out_of_date ) {
my @class_names ;
my ($raw_contents , $contents , $new_contents ) ;
my ($curr_dir) ;
next if ( $f =~ /^$ENV{TRICK_HOME}\/include/ ) ;
next if ( $f =~ /^$ENV{TRICK_HOME}\/trick_source/ ) ;
# clear the processed templates per each file
undef %processed_templates ;
## Read in the entire file contents into raw_contents.
open IN_FILE, $f ;
local $/ ;
$raw_contents = <IN_FILE> ;
# remove all comments, they can cause all kinds of trouble
# leave the line continuation character if present
$raw_contents =~ s/\/\/(?:.*?)(\\)?(\n)/$1\n/sg ;
$raw_contents =~ s/\/\*(?:.*?)\*\/(\s*\\\n)?/$1/sg ;
## The init_attr functions cause problems when we try and wrap them with SWIG.
## We can safely remove them from the header files.
## Remove the friend init_attr functions that appear in multiline define statements
$raw_contents =~ s/\\\n\s*friend\s+void\s+init_attr[^(]+\s*\(\s*\)(\s*);?/$1/sg ;
## Remove the friend init_attr functions outside of multiline define statements
$raw_contents =~ s/friend\s+void\s+init_attr[^(]+\s*\(\s*\)(\s*);?/$1/sg ;
#$raw_contents =~ s/friend\s+void\s+init_attr[^;]+;//sg ;
$raw_contents =~ s/__const/const/sg ;
## For each of the #includes in the out_of_date header file
## create a corresponding %import directive.
foreach (split /^/, $raw_contents) {
if ( /^(\s*\#\s*include\s+)([^\n]+)/ ) {
my ( $include ) = $1 ;
my ( $file_name ) = $2 ;
$file_name =~ s/\s+$// ;
if ( $file_name !~ /\</ ) {
if ( $file_name !~ /\"sim_services/ and $file_name !~ /\"trick_utils/ and $file_name !~ /\"trick\//) {
my $exclude = 0 ;
my $temp_file_name ;
$temp_file_name = $file_name ;
$temp_file_name =~ s/"//g ;
# Check if include file's path begins with dot dot (..)
if ( $temp_file_name =~ /^\.\./ ) {
# include file location is relative to the input file's path.
# Get the absolute path to this include file... use this from now on.
$file_name = abs_path(dirname($f)) . "/" . $temp_file_name ;
# Re-insert double quotes around include file.
#$file_name = "\"" . $file_name . "\"" ;
} elsif ( $temp_file_name !~ /^\// ) {
foreach my $i ( dirname($f) , @include_dirs ) {
if ( -e ( "$i/$temp_file_name" ) ) {
$file_name = abs_path(dirname("$i/$temp_file_name")) . "/" . basename($temp_file_name) ;
last ;
}
}
} else {
$file_name = $temp_file_name ;
}
foreach my $i ( @swig_exclude_dirs ) {
if ( $file_name =~ /^\"\Q$i/ ) {
$exclude = 1 ;
last ;
}
}
$file_name =~ s/^"// ;
$file_name = "\"build" . $file_name . "\"" ;
if ( $exclude == 0 ) {
$file_name =~ s/\.[^\.]+\"/\.i\"/ ;
}
$contents .= "\%import $file_name\n" ;
} else {
$contents .= "\%import(module=\"sim_services\") $file_name\n" ;
}
}
} else {
$contents .= $_ ;
}
}
## Process the contents of the out_of_date header file to create the corresponding SWIG interface.
process_contents( \$contents , \$new_contents , "" , \@class_names ) ;
## Generate a module name and path for the SWIG interface file.
my $md5_sum = md5_hex($f) ;
my ($out_dir) = dirname($f) ;
$out_dir = "build$out_dir" ;
my $out_file ;
$out_file = basename($f) ;
$out_file =~ s/\.h.*$/\.i/ ;
$out_file = "$out_dir/$out_file" ;
if ( ! -e $out_dir ) {
make_path($out_dir) ;
}
## Open the SWIG interface file.
## In the SWIG interface file, write a %module directive that identifies the module.
## In the SWIG interface file, write a #include directive to include trick_swig interface utilities.
## In the SWIG interface file, write a #include directive to include the header
## file to which this interface file corresponds and from which it was derived.
## In the SWIG interface file, create a SWIG interface for each class declared in the corresponding header file using the
## %trick_swig_class_typemap() macro. This macro is defined in swig_class_typedef.i, included by trick_swig.i (see above).
## Write the SWIG interface code (processed header file) and the header file contents.
## Close the SWIG interface file.
open OUT, ">$out_file" ;
print OUT "\%module m$md5_sum\n\n" ;
print OUT "#include \"trick/swig/trick_swig.i\"\n\n" ;
print OUT "
\%insert(\"begin\") \%{
#include <Python.h>
#include <cstddef>
\%}
\%{
#include \"$f\"
\%}\n" ;
print OUT "\n" ;
my %class_typemap_printed ;
foreach my $c ( @class_names ) {
if ( ! exists $class_typemap_printed{$c} ) {
my $c_ = $c ;
$c_ =~ s/\:/_/g ;
print OUT "\%trick_swig_class_typemap($c, $c_)\n" ;
$class_typemap_printed{$c} = 1 ;
}
}
print OUT "\n$new_contents" ;
print OUT "$contents\n" ;
# Add a trick_cast_as macro line for each class parsed in the file. These lines must appear at the bottom of the
# file to ensure they are not in a namespace directive and they are after the #define statements they depend on.
undef %class_typemap_printed ;
foreach my $c ( @class_names ) {
if ( ! exists $class_typemap_printed{$c} ) {
my $c_ = $c ;
$c_ =~ s/\:/_/g ;
print OUT "#ifdef TRICK_SWIG_DEFINED_$c_\n" ;
print OUT "\%trick_cast_as($c, $c_)\n" ;
print OUT "#endif\n" ;
$class_typemap_printed{$c} = 1 ;
}
}
close OUT ;
print "Writing $out_file\n" ;
}
}
sub usage() {
print "$usage\n" ;
exit ;
}
## ================================================================================
## process_contents
##
## Synopsis
##
## Process header file contents for use in the corresponding SWIG interface file.
##
## Parameters
##
## contents_ref
## (IN) reference to header file contents that are to be converted to a SWIG interface.
##
## new_contents_ref
## (OUT) SWIG interface code, derived from the header file contents.
##
## curr_namespace
## (IN) current namespace.
##
## class_names_ref
## (OUT) reference to an array of class and/or struct names encountered when
## processing the header file contents.
##
## Function Dependencies
##
## process_typedef_struct()
## process_namespace()
## process_class()
##
sub process_contents($$$$) {
my ( $contents_ref , $new_contents_ref , $curr_namespace , $class_names_ref ) = @_ ;
while ( $$contents_ref =~ s/^(.*?)(?:($typedef_struct)|
($typedef_const_struct)|
($template_def)|
($namespace_def)|
($class_def))//sx ) {
my ( $non_var ) = $1 ;
my ( $typedef_struct_string ) = $2 ;
my ( $typedef_const_struct_string ) = $3 ;
my ( $template_string ) = $4 ;
my ( $namespace_string ) = $5 ;
my ( $class_string ) = $6 ;
## Handle the case of: non_var
if ( $non_var ne "" ) {
$$new_contents_ref .= $non_var ;
}
##
## Handle the case of: typedef_struct ==> typedef (struct | union ) <name> '{' ...
##
if ( $typedef_struct_string ne "" ) {
process_typedef_struct($typedef_struct_string , $contents_ref, $new_contents_ref, $class_names_ref) ;
}
##
## Handle the case of: typedef_struct ==> typedef const (struct | union ) <name> '{' ...
##
if ( $typedef_const_struct_string ne "" ) {
process_typedef_const_struct($typedef_const_struct_string , $contents_ref) ;
}
##
## Handle the case of: template_def ==> template '<' <template-parameters> '>' class <class-name> ...
## This is required so that templated classes do not match the plain class definition.
##
if ( $template_string ne "" ) {
$$new_contents_ref .= $template_string ;
process_template( $contents_ref , $new_contents_ref ) ;
}
##
## Handle the case of: namespace_def ==> namespace <name>
##
if ( $namespace_string ne "" ) {
process_namespace( $namespace_string , $contents_ref , $new_contents_ref , $curr_namespace ,
$class_names_ref ) ;
}
##
## Handle the case of: class_def ==> ( class | struct ) <class-name> ( '{' | ':' )
##
if ( $class_string ne "" ) {
process_class( $class_string , $contents_ref , $new_contents_ref , $curr_namespace ,
$class_names_ref ) ;
}
}
}
## ================================================================================
## process_template
##
## Synopsis
##
## Process template class definitions. We want to pass through the contents the template
## without processing. We use extract_bracketed to find the whole template definition
## and copy that into new_contents_ref.
##
## Parameters
##
## contents_ref
## (IN) This is a reference to the remainder of the header file (following the
## above string) to be processed.
##
## new_contents_ref
## (OUT) The SWIG code generated so far.
##
sub process_template($$) {
my ( $contents_ref , $new_contents_ref ) = @_ ;
my $extracted ;
if ( $$contents_ref =~ s/^(\s*;)//s ) {
$$new_contents_ref .= $1 ;
} else {
# grab all of the text including the opening bracket.
$$contents_ref =~ s/^(.*?\s*\{)//s ;
$$new_contents_ref .= $1 ;
# grab the rest of the template
($extracted, $$contents_ref) = extract_bracketed( "{" . $$contents_ref , "{}") ;
# remove added extra opening "brace"
$extracted =~ s/^\{// ;
$extracted =~ s/const\s+static/static const/g ;
$$new_contents_ref .= $extracted ;
}
}
## ================================================================================
## process_namespace
##
## Synopsis
##
## Process namespaces found in a header file for use in the corresponding SWIG
## interface file.
##
## Parameters
##
## namespace
## (IN) This is a string of the form: B<namespace> B<I<name>>, that was extracted
## from the header file contents. In the contents there should remain the bracketed
## content to which this namespace applies.
##
## contents_ref
## (IN) This is a reference to the remainder of the header file (following the
## above string) to be processed.
##
## new_contents_ref
## (OUT) The SWIG code generated so far.
##
## curr_namespace
## (IN) current namespace.
##
## class_names_ref
## (OUT) reference to an array of class and/or struct names encountered when
## processing the header file contents.
##
## Function Dependencies
##
## extract_bracketed()
## process_contents()
##
sub process_namespace($$$$$) {
my ( $namespace_string , $contents_ref , $new_contents_ref ,
$curr_namespace , $class_names_ref ) = @_ ;
my $extracted ;
my ($namespace_name) ;
# Get the name of this namespace and add it to the current namespace
$namespace_string =~ /namespace\s+([_A-Za-z]\w*)/sx ;
$namespace_name = $curr_namespace . "$1::" ;
#print "*** namespace_name = $namespace_name ***\n" ;
$$new_contents_ref .= $namespace_string ;
# Extract the contents of the namespace
($extracted, $$contents_ref) = extract_bracketed( $$contents_ref , "{}") ;
# Process the contents of the namespace
process_contents( \$extracted , $new_contents_ref , $namespace_name , $class_names_ref ) ;
# Append whatever wasn't matched in process contents to the new file.
$$new_contents_ref .= $extracted ;
}
## ================================================================================
## process_class
##
## Synopsis
## Process classes declarations found in a header file for use in the corresponding
## SWIG interface file.
##
## Parameters
##
## class_string
## (IN) This is a string of the form: ( class | struct ) <class-name> ( '{' | ':' )
##
## contents_ref
## (IN) This is a reference to the remainder of the header file (following the
## class_string) to be processed.
##
## new_contents_ref
## (OUT) The SWIG code generated so far.
##
## curr_namespace
## (IN) current namespace.
##
## class_names_ref
## (OUT) reference to an array of class and/or struct names encountered when
## processing the header file contents.
##
## <func-depend name="extract_bracketed">
sub process_class($$$$$) {
my ( $class_string , $contents_ref , $new_contents_ref , $curr_namespace ,
$class_names_ref ) = @_ ;
my $extracted ;
my ($class_name) ;
my $template_typedefs ;
## Extract the class_name from the class_string
$class_string =~ /^(?:class|struct)\s+ # keyword class or struct
([_A-Za-z]\w*) # class name
\s*[\{\:]$
/sx or die "Internal error" ;
$class_name = $1 ;
$$new_contents_ref .= $class_string ;
if ( $class_string !~ /\{$/ ) {
$$contents_ref =~ s/^(.*?\s*\{)//s ;
$$new_contents_ref .= $1 ;
}
($extracted, $$contents_ref) = extract_bracketed( "{" . $$contents_ref , "{}") ;
# remove the trailing semicolon because we may append text to the class.
$$contents_ref =~ s/^\s*;//s ;
#remove added extra opening "brace"
$extracted =~ s/^\{// ;
#print "*** extracted = $extracted ***\n" ;
#print "*** contents = $$contents_ref ***\n" ;
# SWIG doesn't like "const static". Change it to "static const"
$extracted =~ s/const\s+static/static const/g ;
# templated variables need to be declared with the SWIG %template directive.
# This loop looks for any templated variables and creates the %template lines.
while ( $extracted =~ s/^(.*?)(?:($template_var_def))//sx ) {
my ( $non_var ) = $1 ;
my ( $template_var_def_str ) = $2 ;
if ( $non_var ne "" ) {
#print "*** non_var = $non_var ***\n" ;
$$new_contents_ref .= $non_var ;
}
if ( $template_var_def_str ne "" ) {
# if there is a whiff of const in the template we are punting.
if ( $template_var_def_str !~ /^const|[<,\s]const\s/ ) {
#print "*** template_var = $template_var_def_str ***\n" ;
$template_var_def_str =~ /(.*?)([_A-Za-z]\w*)\s*;/s ;
my ($template_full_type) = $1 ;
my ($var_name) = $2 ;
#print "*** var_name = $var_name ***\n" ;
$$new_contents_ref .= $template_var_def_str ;
$template_full_type =~ /([_A-Za-z][:\w]*)\s*</ ;
my ($template_type) = $1 ;
# ignore some STL types and types that involve std::wstring
if ( (( $stls and ! exists $stl_names{$template_type})
or ( !$stls and ! exists $all_stl_names{$template_type} ))
and $template_full_type !~ /(std::)?wstring/ ) {
my ($template_type_no_sp) = $template_full_type ;
$template_type_no_sp =~ s/\s//g ;
#print "*** template_type_no_sp = $template_type_no_sp ***\n" ;
if ( ! exists $processed_templates{$template_type_no_sp} ) {
$$new_contents_ref .= "\n#define TRICK_SWIG_TEMPLATE_$class_name${var_name}_template\n" ;
$template_typedefs .= "\n#ifdef TRICK_SWIG_TEMPLATE_$class_name${var_name}_template\n" ;
$template_typedefs .= "\%template ($class_name${var_name}_template) $template_full_type ;\n" ;
$template_typedefs .= "#undef TRICK_SWIG_TEMPLATE_$class_name${var_name}_template\n" ;
$template_typedefs .= "#endif\n" ;
$processed_templates{$template_type_no_sp} = 1 ;
}
}
}
}
}
#print "*** unprocessed extracted = $extracted ***\n" ;
push @$class_names_ref , "$curr_namespace$class_name" ;
# write the class contents and semicolon to ensure any template declarations below are after the semicolon.
$$new_contents_ref .= $extracted . ";\n" ;
# write out the templated variable declaration lines found in this class.
$$new_contents_ref .= $template_typedefs ;
my $c_ = "$curr_namespace$class_name" ;
$c_ =~ s/\:/_/g ;
# Add a #define line that signals that this class has been processed by swig. Classes excluded in #if 0 blocks will
# not have this #define defined.
$$new_contents_ref .= "#define TRICK_SWIG_DEFINED_$c_" ;
}
## ================================================================================
## process_typedef_struct
##
## Synopsis
##
## Process a type definition of a struct or union to make it suitable as SWIG
## interface code. Extract the struct (or union) name and bracketed contents from
## the header file text (typedef_struct_string and contents_ref) . Record the
## extracted names in the list referenced by class_names_ref, and then reconsistute
## the type definition, via the new_contents_ref.
##
## Parameters
##
## typedef_struct_string
## (IN) This is a string of the form:
## "typedef struct [<name>] {" OR "typedef union [<name>] {" </parameter>
##
## contents_ref
## (IN) This is a reference to the remainder of the header file (following the
## above string) to be processed.
##
## new_contents_ref
## (OUT) The SWIG code generated so far.
##
## class_names_ref
## (OUT) reference to an array of class and/or struct names encountered when
## processing the header file contents.
##
sub process_typedef_struct($$$$) {
my ($typedef_struct_string , $contents_ref, $new_contents_ref , $class_names_ref) = @_ ;
my $extracted ;
my ($begin, $tail , $struct_names, @struct_names) ;
#print "*** typedef_struct_string = $typedef_struct_string ***\n" ;
$$new_contents_ref .= $typedef_struct_string ;
$typedef_struct_string =~ s/((?:typedef\s+)?(struct|union)\s* # the words typedef struct|union
([_A-Za-z]\w*)?\s* # optional name
{)//sx ;
$begin = $3 ;
($extracted, $$contents_ref) = extract_bracketed( "{" . $$contents_ref , "{}") ;
#print "*** extracted = $extracted ***\n" ;
#remove added extra opening "brace"
$extracted =~ s/{// ;
$$contents_ref =~ s/^(\s*([\w,\s\*]+)?\s*;)//sx ;
$tail = $1 ;
$struct_names = $2 ;
$struct_names =~ s/\s//g ;
@struct_names = split /,/ , $struct_names ;
if ( $begin ne "" ) {
push @$class_names_ref , $begin ;
}
foreach my $s ( @struct_names ) {
if ( $s !~ /\*/ ) {
push @$class_names_ref , $s ;
}
}
$$new_contents_ref .= $extracted . $tail ;
}
## ================================================================================
## process_typedef_const_struct
##
## Synopsis
##
## Process a typedef const struct definition. SWIG doesn't like this construct.
## If we find one, we ignore the contents by finding the end of the definition
## and throwing it all out.
##
sub process_typedef_const_struct($$$$) {
my ($typedef_const_struct_string , $contents_ref ) = @_ ;
my $extracted ;
($extracted, $$contents_ref) = extract_bracketed( "{" . $$contents_ref , "{}") ;
$$contents_ref =~ s/^(\s*([\w,\s\*]+)?\s*;)//sx ;
}
__END__