2018-02-05 13:38:02 +01:00
#!/usr/bin/env perl
2017-03-17 10:47:01 +01:00
use strict ;
use warnings ;
use File::Basename ;
2017-05-23 09:34:27 +02:00
use File::Spec ;
2017-04-27 10:38:12 +02:00
use Getopt::Long ;
2017-05-23 09:34:27 +02:00
use YAML::Tiny ;
2017-05-22 09:01:45 +02:00
no if $] >= 5.018000 , warnings = > 'experimental::smartmatch' ;
2017-03-17 10:47:01 +01:00
2017-04-07 08:50:57 +02:00
use constant PRIVATE = > 0 ;
use constant PROTECTED = > 1 ;
use constant PUBLIC = > 2 ;
2017-05-18 07:50:23 +02:00
use constant STRICT = > 10 ;
use constant UNSTRICT = > 11 ;
2017-06-12 09:00:04 +02:00
use constant MULTILINE_NO = > 20 ;
use constant MULTILINE_METHOD = > 21 ;
use constant MULTILINE_CONDITIONAL_STATEMENT = > 22 ;
2017-12-19 20:40:43 -04:00
use constant CODE_SNIPPET = > 30 ;
use constant CODE_SNIPPET_CPP = > 31 ;
2017-04-27 10:38:12 +02:00
# read arguments
my $ debug = 0 ;
2018-05-17 22:54:18 -04:00
my $ sip_output = '' ;
my $ python_output = '' ;
2018-01-16 11:21:13 -04:00
#my $SUPPORT_TEMPLATE_DOCSTRING = 0;
#die("usage: $0 [-debug] [-template-doc] headerfile\n") unless GetOptions ("debug" => \$debug, "template-doc" => \$SUPPORT_TEMPLATE_DOCSTRING) && @ARGV == 1;
2018-05-17 22:54:18 -04:00
die ( "usage: $0 [-debug] [-sip_output FILE] [-python_output FILE] headerfile\n" )
unless GetOptions ( "debug" = > \ $ debug , "sip_output=s" = > \ $ sip_output , "python_output=s" = > \ $ python_output ) && @ ARGV == 1 ;
2017-04-27 10:38:12 +02:00
my $ headerfile = $ ARGV [ 0 ] ;
2017-03-27 09:27:12 +02:00
2017-04-27 10:38:12 +02:00
# read file
2017-04-20 11:18:29 +02:00
open ( my $ handle , "<" , $ headerfile ) || die "Couldn't open '" . $ headerfile . "' for reading because: " . $! ;
2017-05-29 11:26:44 +02:00
chomp ( my @ INPUT_LINES = <$handle> ) ;
2017-04-20 11:18:29 +02:00
close $ handle ;
2017-03-17 10:47:01 +01:00
2017-05-23 09:34:27 +02:00
# config
my $ cfg_file = File::Spec - > catfile ( dirname ( __FILE__ ) , 'sipify.yaml' ) ;
my $ yaml = YAML::Tiny - > read ( $ cfg_file ) ;
my $ SIP_CONFIG = $ yaml - > [ 0 ] ;
2017-03-27 09:27:12 +02:00
# contexts
my $ SIP_RUN = 0 ;
my $ HEADER_CODE = 0 ;
2017-04-29 16:16:58 +02:00
my @ ACCESS = ( PUBLIC ) ;
2017-05-22 09:01:45 +02:00
my @ CLASSNAME = ( ) ;
2017-06-13 07:53:15 +02:00
my @ DECLARED_CLASSES = ( ) ;
2017-05-19 21:56:31 +02:00
my @ EXPORTED = ( 0 ) ;
2017-06-12 09:00:04 +02:00
my $ MULTILINE_DEFINITION = MULTILINE_NO ;
2017-05-29 11:26:44 +02:00
my $ ACTUAL_CLASS = '' ;
2017-06-12 09:00:04 +02:00
my $ PYTHON_SIGNATURE = '' ;
2017-05-29 11:26:44 +02:00
2018-05-24 21:21:14 +10:00
my $ INDENT = '' ;
2017-05-29 11:26:44 +02:00
my $ COMMENT = '' ;
2017-12-06 19:17:07 -04:00
my $ COMMENT_PARAM_LIST = 0 ;
2018-01-12 20:28:31 -04:00
my $ COMMENT_LAST_LINE_NOTE_WARNING = 0 ;
my $ COMMENT_CODE_SNIPPET = 0 ;
2018-01-15 21:09:07 -04:00
my $ COMMENT_TEMPLATE_DOCSTRING = 0 ;
2017-05-29 11:26:44 +02:00
my $ GLOB_IFDEF_NESTING_IDX = 0 ;
my @ GLOB_BRACKET_NESTING_IDX = ( 0 ) ;
my $ PRIVATE_SECTION_LINE = '' ;
my $ RETURN_TYPE = '' ;
my $ IS_OVERRIDE = 0 ;
2017-06-08 08:03:09 +02:00
my $ IF_FEATURE_CONDITION = '' ;
2017-05-29 11:26:44 +02:00
my % QFLAG_HASH ;
my $ LINE_COUNT = @ INPUT_LINES ;
my $ LINE_IDX = 0 ;
my $ LINE ;
my @ OUTPUT = ( ) ;
sub read_line {
my $ new_line = $ INPUT_LINES [ $ LINE_IDX ] ;
$ LINE_IDX + + ;
2018-01-09 11:17:02 -04:00
$ debug == 0 or print sprintf ( 'LIN:%d DEPTH:%d ACC:%d BRCK:%d SIP:%d MLT:%d OVR: %d CLSS: %s/%d' ,
2017-05-29 11:26:44 +02:00
$ LINE_IDX ,
$# ACCESS ,
$ ACCESS [ $# ACCESS ] ,
$ GLOB_BRACKET_NESTING_IDX [ $# GLOB_BRACKET_NESTING_IDX ] ,
$ SIP_RUN ,
$ MULTILINE_DEFINITION ,
2018-01-09 11:17:02 -04:00
$ IS_OVERRIDE ,
2017-05-29 11:26:44 +02:00
$ ACTUAL_CLASS ,
$# CLASSNAME ) . " :: " . $ new_line . "\n" ;
return $ new_line ;
}
2017-03-17 10:47:01 +01:00
2017-05-29 11:26:44 +02:00
sub write_output {
my ( $ dbg_code , $ out ) = @ _ ;
if ( $ debug == 1 ) {
$ dbg_code = sprintf ( "%d %-4s :: " , $ LINE_IDX , $ dbg_code ) ;
}
else {
$ dbg_code = '' ;
}
2017-06-08 08:03:09 +02:00
push @ OUTPUT , "%If ($IF_FEATURE_CONDITION)\n" if $ IF_FEATURE_CONDITION ne '' ;
2017-05-29 11:26:44 +02:00
push @ OUTPUT , $ dbg_code . $ out ;
2017-06-08 08:03:09 +02:00
push @ OUTPUT , "%End\n" if $ IF_FEATURE_CONDITION ne '' ;
$ IF_FEATURE_CONDITION = '' ;
2017-05-29 11:26:44 +02:00
}
2017-03-17 10:47:01 +01:00
2017-05-29 11:26:44 +02:00
sub dbg_info {
2017-06-06 15:50:31 +02:00
my $ info = $ _ [ 0 ] ;
2017-05-29 11:26:44 +02:00
if ( $ debug == 1 ) {
2017-06-06 15:50:31 +02:00
push @ OUTPUT , $ info . "\n" ;
print $ LINE_IDX . " " . @ ACCESS . " " . $ SIP_RUN . " " . $ MULTILINE_DEFINITION . " " . $ info . "\n" ;
2017-05-29 11:26:44 +02:00
}
2017-04-27 10:38:12 +02:00
}
2017-05-29 11:26:44 +02:00
2017-06-06 08:02:31 +02:00
sub exit_with_error {
die "! Sipify error in $headerfile at line :: $LINE_IDX\n! $_[0]\n" ;
}
2017-05-29 11:26:44 +02:00
sub write_header_footer {
2017-09-30 13:39:54 +02:00
# small hack to turn files src/core/3d/X.h to src/core/./3d/X.h
# otherwise "sip up to date" test fails. This is because the test uses %Include entries
# and over there we have to use ./3d/X.h entries because SIP parser does not allow a number
# as the first letter of a relative path
my $ headerfile_x = $ headerfile ;
$ headerfile_x =~ s/src\/core\/3d/src\/core\/.\/3d/ ;
2017-05-29 11:26:44 +02:00
push @ OUTPUT , "/************************************************************************\n" ;
push @ OUTPUT , " * This file has been generated automatically from *\n" ;
push @ OUTPUT , " * *\n" ;
2017-09-30 13:39:54 +02:00
push @ OUTPUT , sprintf " * %-*s *\n" , 68 , $ headerfile_x ;
2017-05-29 11:26:44 +02:00
push @ OUTPUT , " * *\n" ;
push @ OUTPUT , " * Do not edit manually ! Edit header and run scripts/sipify.pl again *\n" ;
push @ OUTPUT , " ************************************************************************/\n" ;
2017-04-27 10:38:12 +02:00
}
2017-05-29 11:26:44 +02:00
sub processDoxygenLine {
2017-05-23 09:34:27 +02:00
my $ line = $ _ [ 0 ] ;
2017-12-19 16:34:12 -04:00
2017-12-19 20:40:43 -04:00
# detect code snippet
if ( $ line =~ m/\\code(\{\.(\w+)\})?/ ) {
my $ codelang = "" ;
$ codelang = " $2" if ( defined $ 2 ) ;
2018-01-12 20:28:31 -04:00
$ COMMENT_CODE_SNIPPET = CODE_SNIPPET ;
$ COMMENT_CODE_SNIPPET = CODE_SNIPPET_CPP if ( $ codelang =~ m/cpp/ ) ;
2017-12-19 16:34:12 -04:00
$ codelang =~ s/py/python/ ;
2018-01-12 20:28:31 -04:00
return "\n" if ( $ COMMENT_CODE_SNIPPET == CODE_SNIPPET_CPP ) ;
2017-12-19 20:40:43 -04:00
return ".. code-block::$codelang\n\n" ;
2017-12-19 16:34:12 -04:00
}
if ( $ line =~ m/\\endcode/ ) {
2018-01-12 20:28:31 -04:00
$ COMMENT_CODE_SNIPPET = 0 ;
2017-12-19 16:34:12 -04:00
return "\n" ;
}
2018-01-12 20:28:31 -04:00
if ( $ COMMENT_CODE_SNIPPET != 0 ) {
if ( $ COMMENT_CODE_SNIPPET == CODE_SNIPPET_CPP ) {
2017-12-19 20:40:43 -04:00
return "" ;
} else {
2017-12-19 21:09:15 -04:00
if ( $ line ne '' ) {
return " $line\n" ;
} else {
return "\n" ;
}
2017-12-19 20:40:43 -04:00
}
2017-12-19 16:34:12 -04:00
}
2017-12-15 10:08:49 -04:00
# remove prepending spaces
$ line =~ s/^\s+//g ;
2017-05-23 09:34:27 +02:00
# remove \a formatting
$ line =~ s/\\a (.+?)\b/``$1``/g ;
# replace :: with . (changes c++ style namespace/class directives to Python style)
$ line =~ s/::/./g ;
# replace nullptr with None (nullptr means nothing to Python devs)
$ line =~ s/\bnullptr\b/None/g ;
2018-05-24 21:21:14 +10:00
# if inside multi-line parameter, ensure additional lines are indented
if ( $ line ne '' ) {
if ( $ line !~ m/\s*[\\:]?(param|note|since|return|deprecated|warning)/ ) {
$ line = "$INDENT$line" ;
}
}
else
{
$ INDENT = '' ;
}
2017-05-23 09:34:27 +02:00
# replace \returns with :return:
2018-05-24 21:21:14 +10:00
if ( $ line =~ m/\\return(s)?/ ) {
$ line =~ s/\s*\\return(s)?/\n:return:/ ;
$ INDENT = ' ' x ( index ( $ line , ':' , 4 ) + 1 ) ;
}
2017-12-06 19:17:07 -04:00
2017-12-19 16:34:12 -04:00
# params
2017-12-06 19:17:07 -04:00
if ( $ line =~ m/\\param / ) {
2017-12-15 21:49:36 -04:00
$ line =~ s/\s*\\param (\w+)\b/:param $1:/g ;
2018-05-24 21:21:14 +10:00
$ INDENT = ' ' x ( index ( $ line , ':' , 2 ) + 2 ) ;
2018-01-12 21:09:24 -04:00
if ( $ line =~ m/^:param/ ) {
if ( $ COMMENT_PARAM_LIST == 0 )
{
$ line = "\n$line" ;
}
$ COMMENT_PARAM_LIST = 1 ;
$ COMMENT_LAST_LINE_NOTE_WARNING = 0 ;
2017-12-06 19:17:07 -04:00
}
}
2018-01-09 17:26:37 -04:00
if ( $ line =~ m/^\s*[\\@]brief/ ) {
$ line =~ s/[\\@]brief// ;
2018-05-24 21:21:14 +10:00
$ INDENT = '' ;
2018-01-09 17:26:37 -04:00
if ( $ line =~ m/^\s*$/ ) {
return "" ;
}
}
2017-05-23 09:34:27 +02:00
if ( $ line =~ m/[\\@](ingroup|class)/ ) {
2018-05-24 21:21:14 +10:00
$ INDENT = '' ;
2018-01-09 17:26:37 -04:00
return "" ;
2017-05-23 09:34:27 +02:00
}
if ( $ line =~ m/\\since .*?([\d\.]+)/i ) {
2018-05-24 21:21:14 +10:00
$ INDENT = ' ' ;
2017-12-06 14:26:43 -04:00
return "\n.. versionadded:: $1\n" ;
2017-05-23 09:34:27 +02:00
}
2018-02-28 13:27:03 +10:00
if ( $ line =~ m/\\deprecated (.*)/i ) {
2018-05-24 21:21:14 +10:00
$ INDENT = ' ' ;
2018-02-28 13:27:03 +10:00
return "\n.. deprecated:: $1\n" ;
}
2018-01-09 17:26:37 -04:00
2017-12-16 15:14:33 -04:00
# create links in see also
2018-01-09 17:26:37 -04:00
if ( $ line =~ m/\\see +(\w+(\.\w+)*)(\([^()]*\))?/ ) {
2017-12-05 19:48:24 -04:00
my $ seealso = $ 1 ;
2018-01-09 17:26:37 -04:00
my $ seeline = '' ;
dbg_info ( "see also: $seealso" ) ;
if ( $ seealso =~ m/^Qgs[A-Z]\w+(\([^()]*\))?$/ ) {
dbg_info ( "\\see py:class" ) ;
$ seeline = ":py:class:`$seealso`" ;
2017-12-05 19:48:24 -04:00
}
2018-01-09 17:26:37 -04:00
elsif ( $ seealso =~ m/^(Qgs[A-Z]\w+)\.(\w+)(\([^()]*\))?$/ ) {
dbg_info ( "\\see py:func with param" ) ;
$ seeline = ":py:func:`$1.$2`" ;
2017-12-05 19:48:24 -04:00
}
2018-01-09 17:26:37 -04:00
elsif ( $ seealso =~ m/^[a-z]\w+(\([^()]*\))?$/ ) {
dbg_info ( "\\see py:func" ) ;
$ seeline = ":py:func:`$seealso`" ;
2017-12-05 19:48:24 -04:00
}
2018-01-09 17:26:37 -04:00
if ( $ line =~ m/^\s*\\see/ ) {
if ( $ seeline ne '' ) {
return "\n.. seealso:: $seeline\n" ;
} else {
return "\n.. seealso:: $seealso\n" ;
}
} else {
if ( $ seeline ne '' ) {
$ line =~ s/\\see +(\w+(\.\w+)*(\(\))?)/$seeline/ ;
} else {
$ line =~ s/\\see/see/ ;
}
2017-12-05 19:48:24 -04:00
}
2017-05-23 09:34:27 +02:00
}
2018-01-09 17:26:37 -04:00
else
{
# create links in plain text too (less performant)
if ( $ line =~ m/\b(Qgs[A-Z]\w+)\b(\.?$|[^\w]{2})/ ) {
if ( defined $ ACTUAL_CLASS && $ 1 !~ $ ACTUAL_CLASS ) {
$ line =~ s/\b(Qgs[A-Z]\w+)\b(\.?$|[^\w]{2})/:py:class:`$1`$2/g ;
}
2017-12-19 11:43:12 -04:00
}
2018-01-09 17:26:37 -04:00
$ line =~ s/\b(Qgs[A-Z]\w+\.[a-z]\w+\(\))(\.|\b|$)/:py:func:`$1`/g ;
2017-05-23 09:34:27 +02:00
}
2018-01-09 17:26:37 -04:00
2017-05-23 09:34:27 +02:00
if ( $ line =~ m/[\\@]note (.*)/ ) {
2018-01-12 20:28:31 -04:00
$ COMMENT_LAST_LINE_NOTE_WARNING = 1 ;
2018-05-24 21:21:14 +10:00
$ INDENT = '' ;
2017-12-06 14:26:43 -04:00
return "\n.. note::\n\n $1\n" ;
2017-05-23 09:34:27 +02:00
}
2017-12-23 14:30:08 +10:00
if ( $ line =~ m/[\\@]warning (.*)/ ) {
2018-05-24 21:21:14 +10:00
$ INDENT = '' ;
2018-01-12 20:28:31 -04:00
$ COMMENT_LAST_LINE_NOTE_WARNING = 1 ;
2017-12-23 14:30:08 +10:00
return "\n.. warning::\n\n $1\n" ;
2017-05-23 09:34:27 +02:00
}
2018-01-09 17:26:37 -04:00
2018-01-12 20:28:31 -04:00
if ( $ line !~ m/^\s*$/ ) {
if ( $ COMMENT_LAST_LINE_NOTE_WARNING == 1 ) {
dbg_info ( "prepend spaces for multiline warning/note xx$line" ) ;
$ line = " $line" ;
}
} else {
$ COMMENT_LAST_LINE_NOTE_WARNING = 0 ;
2017-05-23 09:34:27 +02:00
}
return "$line\n" ;
}
2017-05-18 14:16:42 +02:00
sub detect_and_remove_following_body_or_initializerlist {
2017-06-06 11:01:16 +02:00
# https://regex101.com/r/ZaP3tC/8
2017-06-12 09:00:04 +02:00
my $ python_signature = '' ;
2017-04-30 13:26:45 +02:00
do { no warnings 'uninitialized' ;
2017-06-06 11:01:16 +02:00
if ( $ LINE =~ m/^(\s*)?((?:(?:explicit|static|const|unsigned|virtual)\s+)*)(([\w:]+(<.*?>)?\s+[*&]?)?(~?\w+|(\w+::)?operator.{1,2})\s*\(([\w=()\/ ,&*<>."-]|::)*\)( +(?:const|SIP_[\w_]+?))*)\s*((\s*[:,]\s+\w+\(.*\))*\s*\{.*\}\s*(?:SIP_[\w_]+)?;?|(?!;))(\s*\/\/.*)?$/
2017-05-29 11:26:44 +02:00
|| $ LINE =~ m/SIP_SKIP\s*(?!;)\s*(\/\/.*)?$/
|| $ LINE =~ m/^\s*class.*SIP_SKIP/ ) {
2017-04-30 13:26:45 +02:00
dbg_info ( "remove constructor definition, function bodies, member initializing list" ) ;
2017-05-18 16:39:21 +02:00
my $ newline = "$1$2$3;" ;
2017-06-12 09:00:04 +02:00
$ python_signature = remove_following_body_or_initializerlist ( ) unless $ LINE =~ m/{.*}(\s*SIP_\w+)*\s*(\/\/.*)?$/ ;
2017-05-29 11:26:44 +02:00
$ LINE = $ newline ;
2017-05-16 08:39:03 +02:00
}
} ;
2017-06-12 09:00:04 +02:00
return $ python_signature ;
2017-05-16 08:39:03 +02:00
}
2017-05-18 14:16:42 +02:00
sub remove_following_body_or_initializerlist {
2017-06-12 09:00:04 +02:00
my $ python_signature = '' ;
2017-05-16 08:39:03 +02:00
do { no warnings 'uninitialized' ;
dbg_info ( "remove constructor definition, function bodies, member initializing list" ) ;
2017-06-12 09:00:04 +02:00
my $ line = read_line ( ) ;
# python signature
if ( $ line =~ m/^\s*\[\s*(\w+\s*)?\(/ ) {
dbg_info ( "python signature detected" ) ;
my $ nesting_index = 0 ;
while ( $ LINE_IDX < $ LINE_COUNT ) {
$ nesting_index += $ line =~ tr /\[/ / ;
$ nesting_index -= $ line =~ tr /\]/ / ;
if ( $ nesting_index == 0 ) {
if ( $ line =~ m/^(.*);\s*(\/\/.*)?$/ ) {
$ line = $ 1 ; # remove semicolon (added later)
$ python_signature . = "\n$line" ;
return $ python_signature ;
}
last ;
}
$ python_signature . = "\n$line" ;
$ line = read_line ( ) ;
}
2017-05-16 08:39:03 +02:00
}
2017-06-12 09:00:04 +02:00
# member initializing list
while ( $ line =~ m/^\s*[:,]\s+([\w<>]|::)+\(.*?\)/ ) {
dbg_info ( "member initializing list" ) ;
$ line = read_line ( ) ;
}
# body
if ( $ line =~ m/^\s*\{/ ) {
2017-05-16 08:39:03 +02:00
my $ nesting_index = 0 ;
2017-05-29 11:26:44 +02:00
while ( $ LINE_IDX < $ LINE_COUNT ) {
2017-05-16 08:39:03 +02:00
dbg_info ( " remove body" ) ;
2017-06-12 09:00:04 +02:00
$ nesting_index += $ line =~ tr /\{/ / ;
$ nesting_index -= $ line =~ tr /\}/ / ;
2017-05-16 08:39:03 +02:00
if ( $ nesting_index == 0 ) {
last ;
}
2017-06-12 09:00:04 +02:00
$ line = read_line ( ) ;
2017-04-30 13:26:45 +02:00
}
}
} ;
2017-06-12 09:00:04 +02:00
return $ python_signature ;
2017-04-30 13:26:45 +02:00
}
2017-06-02 12:03:19 +02:00
sub fix_annotations {
my $ line = $ _ [ 0 ] ;
# printed annotations
2017-11-01 20:27:39 +01:00
$ line =~ s/\/\/\s*SIP_ABSTRACT\b/\/Abstract\// ;
2017-06-02 12:03:19 +02:00
$ line =~ s/\bSIP_ABSTRACT\b/\/Abstract\// ;
$ line =~ s/\bSIP_ALLOWNONE\b/\/AllowNone\// ;
$ line =~ s/\bSIP_ARRAY\b/\/Array\//g ;
$ line =~ s/\bSIP_ARRAYSIZE\b/\/ArraySize\//g ;
$ line =~ s/\bSIP_DEPRECATED\b/\/Deprecated\//g ;
$ line =~ s/\bSIP_CONSTRAINED\b/\/Constrained\//g ;
$ line =~ s/\bSIP_EXTERNAL\b/\/External\//g ;
$ line =~ s/\bSIP_FACTORY\b/\/Factory\// ;
$ line =~ s/\bSIP_IN\b/\/In\//g ;
$ line =~ s/\bSIP_INOUT\b/\/In,Out\//g ;
$ line =~ s/\bSIP_KEEPREFERENCE\b/\/KeepReference\// ;
$ line =~ s/\bSIP_NODEFAULTCTORS\b/\/NoDefaultCtors\// ;
$ line =~ s/\bSIP_OUT\b/\/Out\//g ;
$ line =~ s/\bSIP_RELEASEGIL\b/\/ReleaseGIL\// ;
$ line =~ s/\bSIP_TRANSFER\b/\/Transfer\//g ;
$ line =~ s/\bSIP_TRANSFERBACK\b/\/TransferBack\// ;
$ line =~ s/\bSIP_TRANSFERTHIS\b/\/TransferThis\// ;
$ line =~ s/SIP_PYNAME\(\s*(\w+)\s*\)/\/PyName=$1\// ;
2017-06-22 20:50:28 +10:00
$ line =~ s/SIP_VIRTUALERRORHANDLER\(\s*(\w+)\s*\)/\/VirtualErrorHandler=$1\// ;
2018-05-15 12:32:50 +10:00
$ line =~ s/SIP_THROW\(\s*(\w+)\s*\)/throw\( $1 \)/ ;
2017-06-02 12:03:19 +02:00
# combine multiple annotations
2018-01-31 12:34:34 +01:00
# https://regex101.com/r/uvCt4M/4
2017-06-02 12:03:19 +02:00
do { no warnings 'uninitialized' ;
2018-01-31 12:34:34 +01:00
$ line =~ s/\/([\w,]+(=\w+)?)\/\s*\/([\w,]+(=\w+)?)\//\/$1,$3\// ;
2017-06-02 12:03:19 +02:00
( ! $ 3 ) or dbg_info ( "combine multiple annotations -- works only for 2" ) ;
} ;
# unprinted annotations
$ line =~ s/(\w+)(\<(?>[^<>]|(?2))*\>)?\s+SIP_PYALTERNATIVETYPE\(\s*\'?([^()']+)(\(\s*(?:[^()]++|(?2))*\s*\))?\'?\s*\)/$3/g ;
$ line =~ s/=\s+[^=]*?\s+SIP_PYARGDEFAULT\(\s*\'?([^()']+)(\(\s*(?:[^()]++|(?2))*\s*\))?\'?\s*\)/= $1/g ;
# remove argument
if ( $ line =~ m/SIP_PYARGREMOVE/ ) {
dbg_info ( "remove arg" ) ;
2017-06-12 09:00:04 +02:00
if ( $ MULTILINE_DEFINITION != MULTILINE_NO ) {
2017-06-02 12:03:19 +02:00
my $ prev_line = pop ( @ OUTPUT ) =~ s/\n$// r ;
# update multi line status
my $ parenthesis_balance = 0 ;
$ parenthesis_balance += $ prev_line =~ tr /\(/ / ;
$ parenthesis_balance -= $ prev_line =~ tr /\)/ / ;
if ( $ parenthesis_balance == 1 ) {
2017-06-12 09:00:04 +02:00
$ MULTILINE_DEFINITION = MULTILINE_NO ;
2017-06-02 12:03:19 +02:00
}
# concat with above line to bring previous commas
$ line =~ s/^\s+// ;
$ line = "$prev_line $line\n" ;
}
# see https://regex101.com/r/5iNptO/4
$ line =~ s/(?<coma>, +)?(const )?(\w+)(\<(?>[^<>]|(?4))*\>)?\s+[\w&*]+\s+SIP_PYARGREMOVE( = [^()]*(\(\s*(?:[^()]++|(?6))*\s*\))?)?(?(<coma>)|,?)//g ;
2017-07-19 10:34:46 +02:00
$ line =~ s/\(\s+\)/()/ ;
2017-06-02 12:03:19 +02:00
}
$ line =~ s/SIP_FORCE// ;
2018-01-15 21:09:07 -04:00
$ line =~ s/SIP_DOC_TEMPLATE// ;
2018-01-16 11:41:15 -04:00
$ line =~ s/\s+;$/;/ ;
2017-06-02 12:03:19 +02:00
return $ line ;
2017-05-08 07:34:37 +02:00
}
2017-05-10 16:09:20 +02:00
# detect a comment block, return 1 if found
2017-05-18 07:50:23 +02:00
# if STRICT comment block shall begin at beginning of line (no code in front)
sub detect_comment_block {
my % args = ( strict_mode = > STRICT , @ _ ) ;
# dbg_info("detect comment strict:" . $args{strict_mode} );
2017-12-06 19:17:07 -04:00
$ COMMENT_PARAM_LIST = 0 ;
2018-05-24 21:21:14 +10:00
$ INDENT = '' ;
2018-01-12 20:28:31 -04:00
$ COMMENT_CODE_SNIPPET = 0 ;
$ COMMENT_LAST_LINE_NOTE_WARNING = 0 ;
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*\/\*/ || $ args { strict_mode } == UNSTRICT && $ LINE =~ m/\/\*/ ) {
2017-05-18 07:50:23 +02:00
dbg_info ( "found comment block" ) ;
2017-05-10 16:09:20 +02:00
do { no warnings 'uninitialized' ;
2017-05-29 11:26:44 +02:00
$ COMMENT = processDoxygenLine ( $ LINE =~ s/^\s*\/\*(\*)?(.*?)\n?$/$2/ r ) ;
2017-05-10 16:09:20 +02:00
} ;
2017-05-29 11:26:44 +02:00
$ COMMENT =~ s/^\s*$// ;
while ( $ LINE !~ m/\*\/\s*(\/\/.*?)?$/ ) {
$ LINE = read_line ( ) ;
$ COMMENT . = processDoxygenLine ( $ LINE =~ s/\s*\*?(.*?)(\/)?\n?$/$1/ r ) ;
2017-05-10 16:09:20 +02:00
}
2017-12-06 14:26:43 -04:00
$ COMMENT =~ s/\n\s+\n/\n\n/ ;
$ COMMENT =~ s/\n{3,}/\n\n/ ;
2017-05-29 11:26:44 +02:00
$ COMMENT =~ s/\n+$// ;
2017-05-10 16:09:20 +02:00
return 1 ;
}
return 0 ;
}
2018-01-09 11:17:02 -04:00
# Detect if line is a non method member declaration
2018-05-17 13:11:10 -04:00
# https://regex101.com/r/gUBZUk/14
2018-01-09 11:17:02 -04:00
sub detect_non_method_member {
2018-05-17 13:11:10 -04:00
return 1 if $ LINE =~ m/^\s*(?:template\s*<\w+>\s+)?(?:(const|mutable|static|friend|unsigned)\s+)*\w+(::\w+)?(<([\w<> *&,()]|::)+>)?(,?\s+\*?\w+( = (-?\d+(\.\d+)?|((QMap|QList)<[^()]+>\(\))|(\w+::)*\w+(\([^()]?\))?)|\[\d+\])?)+;/ ;
2018-01-09 11:17:02 -04:00
return 0 ;
}
2017-04-30 13:26:45 +02:00
2017-05-29 11:26:44 +02:00
write_header_footer ( ) ;
# write some code in front of line to know where the output comes from
$ debug == 0 or push @ OUTPUT , "CODE SIP_RUN MultiLine\n" ;
2017-04-27 10:38:12 +02:00
# main loop
2017-05-29 11:26:44 +02:00
while ( $ LINE_IDX < $ LINE_COUNT ) {
2017-06-12 09:00:04 +02:00
$ PYTHON_SIGNATURE = '' ;
2017-05-29 11:26:44 +02:00
$ ACTUAL_CLASS = $ CLASSNAME [ $# CLASSNAME ] unless $# CLASSNAME < 0 ;
$ LINE = read_line ( ) ;
2017-06-26 09:57:09 +02:00
if ( $ LINE =~ m/^\s*(#define )?+SIP_IF_MODULE\(.*\)$/ ) {
dbg_info ( 'skipping SIP include condition macro' ) ;
next ;
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*SIP_FEATURE\( (\w+) \)(.*)$/ ) {
write_output ( "SF1" , "%Feature $1$2\n" ) ;
2017-04-02 12:53:31 +02:00
next ;
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*SIP_IF_FEATURE\( (\!?\w+) \)(.*)$/ ) {
write_output ( "SF2" , "%If ($1)$2\n" ) ;
2017-04-02 12:53:31 +02:00
next ;
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*SIP_CONVERT_TO_SUBCLASS_CODE(.*)$/ ) {
2017-06-12 09:00:04 +02:00
$ LINE = "%ConvertToSubClassCode$1" ;
# do not go next, let run the "do not process SIP code"
2017-04-02 12:53:31 +02:00
}
2017-07-10 15:41:41 +10:00
if ( $ LINE =~ m/^\s*SIP_VIRTUAL_CATCHER_CODE(.*)$/ ) {
$ LINE = "%VirtualCatcherCode$1" ;
# do not go next, let run the "do not process SIP code"
}
2017-04-02 12:53:31 +02:00
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*SIP_END(.*)$/ ) {
write_output ( "SEN" , "%End$1\n" ) ;
2017-04-02 12:53:31 +02:00
next ;
}
2017-06-08 08:03:09 +02:00
if ( $ LINE =~ s/SIP_WHEN_FEATURE\(\s*(.*?)\s*\)// ) {
dbg_info ( 'found SIP_WHEN_FEATURE' ) ;
$ IF_FEATURE_CONDITION = $ 1 ;
}
2017-06-12 09:00:04 +02:00
# do not process SIP code %XXXCode
2017-06-22 20:50:28 +10:00
if ( $ SIP_RUN == 1 && $ LINE =~ m/^ *% *(VirtualErrorHandler|MappedType|Type(?:Header)?Code|Module(?:Header)?Code|Convert(?:From|To)(?:Type|SubClass)Code|MethodCode)(.*)?$/ ) {
2017-06-12 09:00:04 +02:00
$ LINE = "%$1$2" ;
$ COMMENT = '' ;
dbg_info ( "do not process SIP code" ) ;
while ( $ LINE !~ m/^ *% *End/ ) {
write_output ( "COD" , $ LINE . "\n" ) ;
$ LINE = read_line ( ) ;
2017-06-22 20:50:28 +10:00
$ LINE =~ s/^ *% *(VirtualErrorHandler|MappedType|Type(?:Header)?Code|Module(?:Header)?Code|Convert(?:From|To)(?:Type|SubClass)Code|MethodCode)(.*)?$/%$1$2/ ;
2017-06-12 09:00:04 +02:00
$ LINE =~ s/^\s*SIP_END(.*)$/%End$1/ ;
}
$ LINE =~ s/^\s*% End/%End/ ;
write_output ( "COD" , $ LINE . "\n" ) ;
next ;
}
2017-03-17 10:47:01 +01:00
# Skip preprocessor stuff
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*#/ ) {
2017-04-26 07:47:58 +02:00
# skip #if 0 blocks
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*#if (0|defined\(Q_OS_WIN\))/ ) {
2017-05-16 08:39:03 +02:00
dbg_info ( "skipping #if $1 block" ) ;
2017-04-26 07:47:58 +02:00
my $ nesting_index = 0 ;
2017-05-29 11:26:44 +02:00
while ( $ LINE_IDX < $ LINE_COUNT ) {
$ LINE = read_line ( ) ;
if ( $ LINE =~ m/^\s*#if(def)?\s+/ ) {
2017-04-26 07:47:58 +02:00
$ nesting_index + + ;
}
2017-05-29 11:26:44 +02:00
elsif ( $ nesting_index == 0 && $ LINE =~ m/^\s*#(endif|else)/ ) {
$ COMMENT = '' ;
2017-04-26 07:47:58 +02:00
last ;
}
2017-05-29 11:26:44 +02:00
elsif ( $ nesting_index != 0 && $ LINE =~ m/^\s*#(endif)/ ) {
2017-04-26 07:47:58 +02:00
$ nesting_index - - ;
}
}
next ;
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*#ifdef SIP_RUN/ ) {
2017-03-27 09:27:12 +02:00
$ SIP_RUN = 1 ;
2017-04-29 16:16:58 +02:00
if ( $ ACCESS [ $# ACCESS ] == PRIVATE ) {
dbg_info ( "writing private content" ) ;
2017-10-09 08:44:44 +02:00
write_output ( "PRV1" , $ PRIVATE_SECTION_LINE . "\n" ) if $ PRIVATE_SECTION_LINE ne '' ;
$ PRIVATE_SECTION_LINE = '' ;
2017-03-27 09:27:12 +02:00
}
next ;
}
2017-03-28 09:06:45 +02:00
if ( $ SIP_RUN == 1 ) {
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*#endif/ ) {
if ( $ GLOB_IFDEF_NESTING_IDX == 0 ) {
2017-03-27 09:27:12 +02:00
$ SIP_RUN = 0 ;
next ;
}
else {
2017-05-29 11:26:44 +02:00
$ GLOB_IFDEF_NESTING_IDX - - ;
2017-03-27 09:27:12 +02:00
}
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*#if(def)?\s+/ ) {
$ GLOB_IFDEF_NESTING_IDX + + ;
2017-03-27 09:27:12 +02:00
}
# if there is an else at this level, code will be ignored i.e. not SIP_RUN
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*#else/ && $ GLOB_IFDEF_NESTING_IDX == 0 ) {
while ( $ LINE_IDX < $ LINE_COUNT ) {
$ LINE = read_line ( ) ;
if ( $ LINE =~ m/^\s*#if(def)?\s+/ ) {
$ GLOB_IFDEF_NESTING_IDX + + ;
2017-03-27 09:27:12 +02:00
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^\s*#endif/ ) {
if ( $ GLOB_IFDEF_NESTING_IDX == 0 ) {
$ COMMENT = '' ;
2017-03-27 09:27:12 +02:00
$ SIP_RUN = 0 ;
last ;
}
else {
2017-05-29 11:26:44 +02:00
$ GLOB_IFDEF_NESTING_IDX - - ;
2017-03-27 09:27:12 +02:00
}
}
}
next ;
}
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^\s*#ifndef SIP_RUN/ ) {
2017-03-27 09:27:12 +02:00
# code is ignored here
2017-05-29 11:26:44 +02:00
while ( $ LINE_IDX < $ LINE_COUNT ) {
$ LINE = read_line ( ) ;
if ( $ LINE =~ m/^\s*#if(def)?\s+/ ) {
$ GLOB_IFDEF_NESTING_IDX + + ;
2017-03-27 09:27:12 +02:00
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^\s*#else/ && $ GLOB_IFDEF_NESTING_IDX == 0 ) {
2017-03-27 09:27:12 +02:00
# code here will be printed out
2017-04-29 16:16:58 +02:00
if ( $ ACCESS [ $# ACCESS ] == PRIVATE ) {
dbg_info ( "writing private content" ) ;
2017-10-09 08:44:44 +02:00
write_output ( "PRV2" , $ PRIVATE_SECTION_LINE . "\n" ) if $ PRIVATE_SECTION_LINE ne '' ;
$ PRIVATE_SECTION_LINE = '' ;
2017-03-27 09:27:12 +02:00
}
$ SIP_RUN = 1 ;
last ;
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^\s*#endif/ ) {
if ( $ GLOB_IFDEF_NESTING_IDX == 0 ) {
$ COMMENT = '' ;
2017-03-27 09:27:12 +02:00
$ SIP_RUN = 0 ;
last ;
}
else {
2017-05-29 11:26:44 +02:00
$ GLOB_IFDEF_NESTING_IDX - - ;
2017-03-27 09:27:12 +02:00
}
}
}
next ;
}
else {
2017-03-17 10:47:01 +01:00
next ;
}
}
2017-03-27 09:27:12 +02:00
# TYPE HEADER CODE
2017-03-28 09:06:45 +02:00
if ( $ HEADER_CODE && $ SIP_RUN == 0 ) {
2017-03-27 09:27:12 +02:00
$ HEADER_CODE = 0 ;
2017-05-29 11:26:44 +02:00
write_output ( "HCE" , "%End\n" ) ;
2017-03-27 09:27:12 +02:00
}
2017-03-17 10:47:01 +01:00
# Skip forward declarations
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*(class|struct) \w+(?<external> *SIP_EXTERNAL)?;\s*(\/\/.*)?$/ ) {
2017-05-24 14:46:43 +02:00
if ( $+ { external } ) {
dbg_info ( 'do not skip external forward declaration' ) ;
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-05-24 14:46:43 +02:00
}
else
{
dbg_info ( 'skipping forward declaration' ) ;
next ;
}
2017-03-17 10:47:01 +01:00
}
2017-06-20 13:34:27 +02:00
# skip friends
if ( $ LINE =~ m/^\s*friend class \w+/ ) {
next ;
}
2018-05-15 17:37:18 -04:00
# insert metaoject for Q_GADGET
if ( $ LINE =~ m/^\s*Q_GADGET\b.*?$/ ) {
2018-05-16 14:25:01 -04:00
if ( $ LINE !~ m/SIP_SKIP/ ) {
2018-05-17 22:54:18 -04:00
dbg_info ( 'Q_GADGET' ) ;
2018-05-16 14:25:01 -04:00
write_output ( "HCE" , " public:\n" ) ;
write_output ( "HCE" , " static const QMetaObject staticMetaObject;\n\n" ) ;
}
2018-05-15 17:37:18 -04:00
next ;
}
2018-05-18 09:25:12 -08:00
if ( $ LINE =~ m/Q_(ENUM|FLAG)\(\s*(\w+)\s*\)/ ) {
2018-05-17 22:54:18 -04:00
if ( $ LINE !~ m/SIP_SKIP/ ) {
2018-05-18 09:25:12 -08:00
my $ is_flag = $ 1 eq 'FLAG' ? 1 : 0 ;
my $ enum_helper = "$ACTUAL_CLASS.$2.baseClass = $ACTUAL_CLASS" ;
dbg_info ( "Q_ENUM/Q_FLAG $enum_helper" ) ;
2018-05-17 22:54:18 -04:00
if ( $ python_output ne '' ) {
my $ pl ;
open ( FH , '+<' , $ python_output ) or die $! ;
foreach $ pl ( <FH> ) {
if ( $ pl =~ m/$enum_helper/ ) {
$ enum_helper = '' ;
last ;
}
}
if ( $ enum_helper ne '' ) {
2018-05-18 09:25:12 -08:00
if ( $ is_flag == 1 ) {
# SIP seems to introduce the flags in the module rather than in the class itself
# as a dirty hack, inject directly in module, hopefully we don't have flags with the same name....
$ enum_helper . = "\n$2 = $ACTUAL_CLASS # dirty hack since SIP seems to introduce the flags in module" ;
}
2018-05-17 22:54:18 -04:00
print FH "$enum_helper\n" ;
}
close ( FH ) ;
}
}
next ;
}
2018-05-15 17:37:18 -04:00
# Skip Q_OBJECT, Q_PROPERTY, Q_ENUM etc.
if ( $ LINE =~ m/^\s*Q_(OBJECT|ENUMS|ENUM|FLAG|PROPERTY|DECLARE_METATYPE|DECLARE_TYPEINFO|NOWARN_DEPRECATED_(PUSH|POP))\b.*?$/ ) {
2017-03-17 10:47:01 +01:00
next ;
}
2017-03-27 09:27:12 +02:00
# SIP_SKIP
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/SIP_SKIP/ ) {
2017-04-30 13:26:45 +02:00
dbg_info ( 'SIP SKIP!' ) ;
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-04-30 13:26:45 +02:00
# if multiline definition, remove previous lines
2017-06-12 09:00:04 +02:00
if ( $ MULTILINE_DEFINITION != MULTILINE_NO ) {
2017-04-30 13:26:45 +02:00
dbg_info ( 'SIP_SKIP with MultiLine' ) ;
my $ opening_line = '' ;
while ( $ opening_line !~ m/^[^()]*\(([^()]*\([^()]*\)[^()]*)*[^()]*$/ ) {
2017-05-29 11:26:44 +02:00
$ opening_line = pop ( @ OUTPUT ) ;
2017-06-06 08:02:31 +02:00
$# OUTPUT >= 0 or exit_with_error ( 'could not reach opening definition' ) ;
2017-04-30 13:26:45 +02:00
}
2017-04-27 10:38:12 +02:00
dbg_info ( "removed multiline definition of SIP_SKIP method" ) ;
2017-06-12 09:00:04 +02:00
$ MULTILINE_DEFINITION = MULTILINE_NO ;
2017-04-24 14:52:10 +02:00
}
2017-04-30 13:26:45 +02:00
# also skip method body if there is one
2017-05-18 14:16:42 +02:00
detect_and_remove_following_body_or_initializerlist ( ) ;
2017-04-30 13:26:45 +02:00
# line skipped, go to next iteration
next ;
2017-03-27 09:27:12 +02:00
}
2017-04-29 16:16:58 +02:00
# Detect comment block
2017-05-10 16:09:20 +02:00
if ( detect_comment_block ( ) ) {
2017-04-29 16:16:58 +02:00
next ;
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*struct(\s+\w+_EXPORT)?\s+\w+$/ ) {
2017-05-22 09:01:45 +02:00
dbg_info ( " going to struct => public" ) ;
push @ CLASSNAME , $ CLASSNAME [ $# CLASSNAME ] ; # fake new class since struct has considered similarly
push @ ACCESS , PUBLIC ;
push @ EXPORTED , $ EXPORTED [ - 1 ] ;
2017-05-29 11:26:44 +02:00
push @ GLOB_BRACKET_NESTING_IDX , 0 ;
2017-05-22 09:01:45 +02:00
}
# class declaration started
2017-06-13 07:53:15 +02:00
# https://regex101.com/r/6FWntP/10
2017-11-01 20:27:39 +01:00
if ( $ LINE =~ m/^(\s*class)\s+([A-Z]+_EXPORT\s+)?(\w+)(\s*\:\s*(public|protected|private)\s+\w+(< *(\w|::)+ *>)?(::\w+(<\w+>)?)*(,\s*(public|protected|private)\s+\w+(< *(\w|::)+ *>)?(::\w+(<\w+>)?)*)*)?(?<annot>\s*\/?\/?\s*SIP_\w+)?\s*?(\/\/.*|(?!;))$/ ) {
2017-05-22 09:01:45 +02:00
dbg_info ( "class definition started" ) ;
push @ ACCESS , PUBLIC ;
push @ EXPORTED , 0 ;
2017-05-29 11:26:44 +02:00
push @ GLOB_BRACKET_NESTING_IDX , 0 ;
2017-05-22 09:01:45 +02:00
my @ template_inheritance_template = ( ) ;
my @ template_inheritance_class = ( ) ;
do { no warnings 'uninitialized' ;
push @ CLASSNAME , $ 3 ;
2017-06-13 07:53:15 +02:00
if ( $# CLASSNAME == 0 ) {
# might be worth to add in-class classes later on
# in case of a tamplate based class declaration
# based on an in-class and in the same file
push @ DECLARED_CLASSES , $ CLASSNAME [ $# CLASSNAME ] ;
}
2017-06-13 07:58:28 +02:00
dbg_info ( "class: " . $ CLASSNAME [ $# CLASSNAME ] ) ;
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/\b[A-Z]+_EXPORT\b/ || $# CLASSNAME != 0 || $ INPUT_LINES [ $ LINE_IDX - 2 ] =~ m/^\s*template</ ) {
2017-05-24 07:22:46 +02:00
# class should be exported except those not at top level or template classes
# if class is not exported, then its methods should be (checked whenever leaving out the class)
$ EXPORTED [ - 1 ] + + ;
}
2017-05-22 09:01:45 +02:00
} ;
2017-05-29 11:26:44 +02:00
$ LINE = "$1 $3" ;
2017-05-22 09:01:45 +02:00
# Inheritance
if ( $ 4 ) {
my $ m = $ 4 ;
2017-06-08 15:30:38 +02:00
$ m =~ s/public +(\w+, *)*(Ui::\w+,? *)+//g ; # remove Ui::xxx public inheritance as the namespace is causing troubles
$ m =~ s/public +//g ;
$ m =~ s/[,:]?\s*private +\w+(::\w+)?//g ;
2017-05-22 09:01:45 +02:00
# detect template based inheritance
2017-06-13 07:53:15 +02:00
while ( $ m =~ /[,:]\s+((?!QList)\w+)< *((\w|::)+) *>/g ) {
2017-05-22 09:01:45 +02:00
dbg_info ( "template class" ) ;
push @ template_inheritance_template , $ 1 ;
push @ template_inheritance_class , $ 2 ;
}
2017-06-13 08:18:27 +02:00
$ m =~ s/(\b(?!QList)\w+)< *((?:\w|::)+) *>/$1${2}Base/g ; # use the typeded as template inheritance
2017-06-13 10:50:23 +02:00
$ m =~ s/(\w+)< *((?:\w|::)+) *>//g ; # remove remaining templates
2017-05-22 09:01:45 +02:00
$ m =~ s/([:,])\s*,/$1/g ;
$ m =~ s/(\s*[:,])?\s*$// ;
2017-05-29 11:26:44 +02:00
$ LINE . = $ m ;
2017-05-22 09:01:45 +02:00
}
if ( defined $+ { annot } )
{
2017-05-29 11:26:44 +02:00
$ LINE . = "$+{annot}" ;
2017-06-02 12:03:19 +02:00
$ LINE = fix_annotations ( $ LINE ) ;
2017-05-22 09:01:45 +02:00
}
2017-05-29 11:26:44 +02:00
$ LINE . = "\n{\n" ;
if ( $ COMMENT !~ m/^\s*$/ ) {
$ LINE . = "%Docstring\n$COMMENT\n%End\n" ;
2017-05-22 09:01:45 +02:00
}
2017-05-29 11:26:44 +02:00
$ LINE . = "\n%TypeHeaderCode\n#include \"" . basename ( $ headerfile ) . "\"" ;
2017-05-22 09:01:45 +02:00
# for template based inheritance, add a typedef to define the base type
# add it to the class and to the TypeHeaderCode
# also include the template header
# see https://www.riverbankcomputing.com/pipermail/pyqt/2015-May/035893.html
2017-06-13 07:53:15 +02:00
while ( @ template_inheritance_template ) {
2017-05-22 09:01:45 +02:00
my $ tpl = pop @ template_inheritance_template ;
my $ cls = pop @ template_inheritance_class ;
2017-05-29 11:26:44 +02:00
$ LINE = "\ntypedef $tpl<$cls> ${tpl}${cls}Base;\n\n$LINE" ;
2017-06-13 07:53:15 +02:00
if ( not $ tpl ~ ~ @ DECLARED_CLASSES ) {
my $ tpl_header = lc $ tpl . ".h" ;
if ( exists $ SIP_CONFIG - > { class_headerfile } - > { $ tpl } ) {
$ tpl_header = $ SIP_CONFIG - > { class_headerfile } - > { $ tpl } ;
}
$ LINE . = "\n#include \"" . $ tpl_header . "\"" ;
}
2017-05-29 11:26:44 +02:00
$ LINE . = "\ntypedef $tpl<$cls> ${tpl}${cls}Base;" ;
2017-05-22 09:01:45 +02:00
}
if ( PRIVATE ~ ~ @ ACCESS && $# ACCESS != 0 ) {
# do not write anything in PRIVATE context and not top level
dbg_info ( "skipping class in private context" ) ;
next ;
}
$ ACCESS [ $# ACCESS ] = PRIVATE ; # private by default
2017-05-29 11:26:44 +02:00
write_output ( "CLS" , "$LINE\n" ) ;
2017-05-22 09:01:45 +02:00
# Skip opening curly bracket, incrementing hereunder
2017-05-29 11:26:44 +02:00
my $ skip = read_line ( ) ;
2017-06-06 08:02:31 +02:00
$ skip =~ m/^\s*{\s*$/ or exit_with_error ( "expecting { after class definition" ) ;
2017-05-29 11:26:44 +02:00
$ GLOB_BRACKET_NESTING_IDX [ $# GLOB_BRACKET_NESTING_IDX ] + + ;
2017-05-22 09:01:45 +02:00
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-05-22 09:01:45 +02:00
$ HEADER_CODE = 1 ;
$ ACCESS [ $# ACCESS ] = PRIVATE ;
next ;
}
2017-04-29 16:16:58 +02:00
# bracket balance in class/struct tree
if ( $ SIP_RUN == 0 ) {
my $ bracket_balance = 0 ;
2017-05-29 11:26:44 +02:00
$ bracket_balance += $ LINE =~ tr /\{/ / ;
$ bracket_balance -= $ LINE =~ tr /\}/ / ;
2017-04-29 16:16:58 +02:00
if ( $ bracket_balance != 0 ) {
2017-05-29 11:26:44 +02:00
$ GLOB_BRACKET_NESTING_IDX [ $# GLOB_BRACKET_NESTING_IDX ] += $ bracket_balance ;
if ( $ GLOB_BRACKET_NESTING_IDX [ $# GLOB_BRACKET_NESTING_IDX ] == 0 ) {
2017-04-29 16:16:58 +02:00
dbg_info ( " going up in class/struct tree" ) ;
2017-05-15 16:30:34 +02:00
if ( $# ACCESS > 0 ) {
2017-05-29 11:26:44 +02:00
pop ( @ GLOB_BRACKET_NESTING_IDX ) ;
2017-04-29 16:16:58 +02:00
pop ( @ ACCESS ) ;
2017-06-06 08:02:31 +02:00
exit_with_error ( "Class $CLASSNAME[$#CLASSNAME] should be exported with appropriate [LIB]_EXPORT macro. If this should not be available in python, wrap it in a `#ifndef SIP_RUN` block." )
2017-06-15 07:39:16 +02:00
if $ EXPORTED [ - 1 ] == 0 and not $ CLASSNAME [ $# CLASSNAME ] ~ ~ $ SIP_CONFIG - > { no_export_macro } ;
2017-05-21 10:09:57 +02:00
pop @ EXPORTED ;
2017-04-29 16:16:58 +02:00
}
2017-05-22 09:01:45 +02:00
pop ( @ CLASSNAME ) ;
2017-05-15 16:30:34 +02:00
if ( $# ACCESS == 0 ) {
2017-04-30 13:26:45 +02:00
dbg_info ( "reached top level" ) ;
2017-04-29 16:16:58 +02:00
# top level should stasy public
$ ACCESS [ $# ACCESS ] = PUBLIC ;
}
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
$ RETURN_TYPE = '' ;
$ PRIVATE_SECTION_LINE = '' ;
2017-04-29 16:16:58 +02:00
}
2017-05-29 11:26:44 +02:00
dbg_info ( "new bracket balance: @GLOB_BRACKET_NESTING_IDX" ) ;
2017-04-29 16:16:58 +02:00
}
}
2017-03-27 09:27:12 +02:00
# Private members (exclude SIP_RUN)
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*private( slots)?:/ ) {
2017-04-29 16:16:58 +02:00
$ ACCESS [ $# ACCESS ] = PRIVATE ;
2017-05-29 11:26:44 +02:00
$ PRIVATE_SECTION_LINE = $ LINE ;
$ COMMENT = '' ;
2017-04-29 16:16:58 +02:00
dbg_info ( "going private" ) ;
2017-03-27 09:27:12 +02:00
next ;
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^\s*(public( slots)?|signals):.*$/ ) {
2017-04-29 16:16:58 +02:00
dbg_info ( "going public" ) ;
$ ACCESS [ $# ACCESS ] = PUBLIC ;
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-04-07 08:50:57 +02:00
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^\s*(protected)( slots)?:.*$/ ) {
2017-04-29 16:16:58 +02:00
dbg_info ( "going protected" ) ;
$ ACCESS [ $# ACCESS ] = PROTECTED ;
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-04-24 08:48:12 +02:00
}
2017-05-29 11:26:44 +02:00
elsif ( $ ACCESS [ $# ACCESS ] == PRIVATE && $ LINE =~ m/SIP_FORCE/ ) {
2017-04-29 16:16:58 +02:00
dbg_info ( "private with SIP_FORCE" ) ;
2017-10-09 08:44:44 +02:00
write_output ( "PRV3" , $ PRIVATE_SECTION_LINE . "\n" ) if $ PRIVATE_SECTION_LINE ne '' ;
$ PRIVATE_SECTION_LINE = '' ;
2017-04-07 08:50:57 +02:00
}
2017-05-22 09:01:45 +02:00
elsif ( PRIVATE ~ ~ @ ACCESS && $ SIP_RUN == 0 ) {
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-04-29 16:16:58 +02:00
next ;
2017-03-27 09:27:12 +02:00
}
2017-04-26 11:31:56 +02:00
# Skip operators
2017-06-01 10:54:33 +02:00
if ( $ ACCESS [ $# ACCESS ] != PRIVATE && $ LINE =~ m/operator(=|<<|>>|->)\s*\(/ ) {
2017-04-29 16:16:58 +02:00
dbg_info ( "skip operator" ) ;
2017-05-18 14:16:42 +02:00
detect_and_remove_following_body_or_initializerlist ( ) ;
2017-03-28 09:06:45 +02:00
next ;
}
2017-03-27 09:27:12 +02:00
# save comments and do not print them, except in SIP_RUN
2017-03-28 09:06:45 +02:00
if ( $ SIP_RUN == 0 ) {
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*\/\// ) {
if ( $ LINE =~ m/^\s*\/\/\!\s*(.*?)\n?$/ ) {
2017-12-06 19:17:07 -04:00
$ COMMENT_PARAM_LIST = 0 ;
2018-05-24 21:21:14 +10:00
$ INDENT = '' ;
2018-01-12 20:28:31 -04:00
$ COMMENT_LAST_LINE_NOTE_WARNING = 0 ;
2017-05-29 11:26:44 +02:00
$ COMMENT = processDoxygenLine ( $ 1 ) ;
$ COMMENT =~ s/\n+$// ;
2017-04-30 14:33:08 +02:00
}
2017-05-29 11:26:44 +02:00
elsif ( $ INPUT_LINES [ $ LINE_IDX - 1 ] !~ m/\*\/.*/ ) {
$ COMMENT = '' ;
2017-04-30 14:33:08 +02:00
}
2017-03-27 09:27:12 +02:00
next ;
}
2017-03-17 10:47:01 +01:00
}
2017-03-27 09:27:12 +02:00
# Enum declaration
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*enum\s+\w+.*?$/ ) {
write_output ( "ENU1" , "$LINE\n" ) ;
if ( $ LINE =~ m/\{((\s*\w+)(\s*=\s*[\w\s\d<|]+.*?)?(,?))+\s*\}/ ) {
2017-04-24 14:52:10 +02:00
# one line declaration
2017-06-06 08:02:31 +02:00
$ LINE !~ m/=/ or exit_with_error ( "spify.pl does not handle enum one liners with value assignment. Use multiple lines instead." ) ;
2017-04-24 14:54:07 +02:00
next ;
2017-04-24 14:52:10 +02:00
}
else
{
2017-05-29 11:26:44 +02:00
$ LINE = read_line ( ) ;
2017-06-06 08:02:31 +02:00
$ LINE =~ m/^\s*\{\s*$/ or exit_with_error ( 'Unexpected content: enum should be followed by {' ) ;
2017-05-29 11:26:44 +02:00
write_output ( "ENU2" , "$LINE\n" ) ;
while ( $ LINE_IDX < $ LINE_COUNT ) {
$ LINE = read_line ( ) ;
2017-05-10 16:09:20 +02:00
if ( detect_comment_block ( ) ) {
next ;
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/\};/ ) {
2017-04-24 14:52:10 +02:00
last ;
}
2017-06-02 12:03:19 +02:00
do { no warnings 'uninitialized' ;
2017-06-08 15:50:48 +02:00
my $ enum_decl = $ LINE =~ s/(\s*\w+)(\s+SIP_\w+(?:\([^()]+\))?)?(?:\s*=\s*(?:[\w\s\d|+-]|::|<<)+.*?)?(,?).*$/$1$2$3/ r ;
2017-06-02 12:03:19 +02:00
$ enum_decl = fix_annotations ( $ enum_decl ) ;
write_output ( "ENU3" , "$enum_decl\n" ) ;
} ;
2017-05-18 07:50:23 +02:00
detect_comment_block ( strict_mode = > UNSTRICT ) ;
2017-03-27 09:27:12 +02:00
}
2017-05-29 11:26:44 +02:00
write_output ( "ENU4" , "$LINE\n" ) ;
2017-04-24 14:52:10 +02:00
# enums don't have Docstring apparently
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
2017-04-24 14:52:10 +02:00
next ;
2017-03-27 09:27:12 +02:00
}
}
2017-06-08 08:03:09 +02:00
$ IS_OVERRIDE = 1 if ( $ LINE =~ m/\boverride\b/ ) ;
2017-06-06 15:50:31 +02:00
# keyword fixes
do { no warnings 'uninitialized' ;
2017-05-21 20:35:27 +02:00
$ LINE =~ s/^(\s*template\s*<)(?:class|typename) (\w+>)(.*)$/$1$2$3/ ;
2017-06-06 15:50:31 +02:00
$ LINE =~ s/\s*\boverride\b// ;
$ LINE =~ s/\s*\bextern \b// ;
2018-02-27 21:32:45 -04:00
$ LINE =~ s/\s*\bMAYBE_UNUSED \b// ;
2018-02-28 13:27:03 +10:00
$ LINE =~ s/\s*\bNODISCARD \b// ;
$ LINE =~ s/\s*\bQ_DECL_DEPRECATED\b// ;
2017-06-06 15:50:31 +02:00
$ LINE =~ s/^(\s*)?(const )?(virtual |static )?inline /$1$2$3/ ;
$ LINE =~ s/\bconstexpr\b/const/ ;
$ LINE =~ s/\bnullptr\b/0/g ;
$ LINE =~ s/\s*=\s*default\b//g ;
} ;
if ( $ LINE =~ /\b\w+_EXPORT\b/ ) {
$ EXPORTED [ - 1 ] + + ;
$ LINE =~ s/\b\w+_EXPORT\s+//g ;
}
2017-04-08 10:06:55 +02:00
# skip non-method member declaration in non-public sections
2017-05-16 08:39:03 +02:00
if ( $ SIP_RUN != 1 &&
$ ACCESS [ $# ACCESS ] != PUBLIC &&
2018-01-09 11:17:02 -04:00
detect_non_method_member ( ) == 1 ) {
2017-04-27 10:38:12 +02:00
dbg_info ( "skip non-method member declaration in non-public sections" ) ;
2017-04-05 09:46:14 +02:00
next ;
}
2017-05-02 20:34:25 +02:00
# remove static const value assignment
2017-06-06 15:50:31 +02:00
# https://regex101.com/r/DyWkgn/6
do { no warnings 'uninitialized' ;
$ LINE !~ m/^\s*const static \w+/ or exit_with_error ( "const static should be written static const in $CLASSNAME[$#CLASSNAME]" ) ;
$ LINE =~ s/^(?<staticconst> *(?<static>static )?const \w+(?:<(?:[\w<>, ]|::)+>)? \w+)(?: = [^()]+?(\((?:[^()]++|(?3))*\))?[^()]*?)?(?<endingchar>[|;]) *(\/\/.*?)?$/$1;/ ;
$ COMMENT = '' if ( defined $+ { staticconst } && ! defined $+ { static } ) ;
if ( defined $+ { endingchar } && $+ { endingchar } =~ m/\|/ ) {
dbg_info ( "multiline const static assignment" ) ;
my $ skip = '' ;
while ( $ skip !~ m/;\s*(\/\/.*?)?$/ ) {
$ skip = read_line ( ) ;
}
2017-05-22 09:01:45 +02:00
}
2017-06-06 15:50:31 +02:00
} ;
2017-05-02 20:34:25 +02:00
2017-04-19 15:49:22 +02:00
# remove struct member assignment
2018-05-17 21:49:07 -04:00
if ( $ SIP_RUN != 1 && $ ACCESS [ $# ACCESS ] == PUBLIC && $ LINE =~ m/^(\s*\w+[\w<> *&:,]* \*?\w+) = [\-\w\:\.]+(\([^()]*\))?\s*;/ ) {
2017-04-27 10:38:12 +02:00
dbg_info ( "remove struct member assignment" ) ;
2017-05-29 11:26:44 +02:00
$ LINE = "$1;" ;
2017-04-19 15:49:22 +02:00
}
2017-04-03 13:53:29 +10:00
# catch Q_DECLARE_FLAGS
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^(\s*)Q_DECLARE_FLAGS\(\s*(.*?)\s*,\s*(.*?)\s*\)\s*$/ ) {
my $ ACTUAL_CLASS = $ CLASSNAME [ $# CLASSNAME ] ;
dbg_info ( "Declare flags: $ACTUAL_CLASS" ) ;
2017-10-01 01:09:50 +02:00
$ LINE = "$1typedef QFlags<${ACTUAL_CLASS}::$3> $2;\n" ;
$ QFLAG_HASH { "${ACTUAL_CLASS}::$2" } = "${ACTUAL_CLASS}::$3" ;
2017-04-03 13:53:29 +10:00
}
# catch Q_DECLARE_OPERATORS_FOR_FLAGS
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^(\s*)Q_DECLARE_OPERATORS_FOR_FLAGS\(\s*(.*?)\s*\)\s*$/ ) {
my $ flag = $ QFLAG_HASH { $ 2 } ;
$ LINE = "$1QFlags<$flag> operator|($flag f1, QFlags<$flag> f2);\n" ;
2017-04-03 13:53:29 +10:00
}
2017-04-19 11:16:09 +02:00
# remove Q_INVOKABLE
2017-05-29 11:26:44 +02:00
$ LINE =~ s/^(\s*)Q_INVOKABLE /$1/ ;
2017-04-19 11:16:09 +02:00
2017-03-27 09:27:12 +02:00
do { no warnings 'uninitialized' ;
2017-03-29 16:15:07 +02:00
# remove keywords
2017-06-06 15:50:31 +02:00
if ( $ IS_OVERRIDE == 1 ) {
2017-04-20 13:58:56 +02:00
# handle multiline definition to add virtual keyword on opening line
2017-06-12 09:00:04 +02:00
if ( $ MULTILINE_DEFINITION != MULTILINE_NO ) {
2017-05-29 11:26:44 +02:00
my $ virtual_line = $ LINE ;
my $ virtual_line_idx = $ LINE_IDX ;
2017-04-27 10:38:12 +02:00
dbg_info ( "handle multiline definition to add virtual keyword on opening line" ) ;
2017-04-20 13:58:56 +02:00
while ( $ virtual_line !~ m/^[^()]*\(([^()]*\([^()]*\)[^()]*)*[^()]*$/ ) {
$ virtual_line_idx - - ;
2017-05-29 11:26:44 +02:00
$ virtual_line = $ INPUT_LINES [ $ virtual_line_idx ] ;
2017-06-06 08:02:31 +02:00
$ virtual_line_idx >= 0 or exit_with_error ( 'could not reach opening definition' ) ;
2017-04-20 13:58:56 +02:00
}
if ( $ virtual_line !~ m/^(\s*)virtual\b(.*)$/ ) {
2017-05-29 11:26:44 +02:00
my $ idx = $# OUTPUT - $ LINE_IDX + $ virtual_line_idx + 2 ;
#print "len: $#OUTPUT line_idx: $LINE_IDX virt: $virtual_line_idx\n"idx: $idx\n$OUTPUT[$idx]\n";
$ OUTPUT [ $ idx ] = $ virtual_line =~ s/^(\s*?)\b(.*)$/$1 virtual $2\n/ r ;
2017-04-20 13:58:56 +02:00
}
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE !~ m/^(\s*)virtual\b(.*)$/ ) {
2017-04-18 11:00:13 +10:00
#sip often requires the virtual keyword to be present, or it chokes on covariant return types
#in overridden methods
2017-06-07 16:16:21 +02:00
dbg_info ( 'adding virtual keyword for overridden method' ) ;
2017-05-29 11:26:44 +02:00
$ LINE =~ s/^(\s*?)\b(.*)$/$1virtual $2\n/ ;
2017-04-18 11:00:13 +10:00
}
2017-04-17 11:03:02 +10:00
}
2017-05-18 14:16:42 +02:00
2017-04-26 08:22:27 +02:00
# remove constructor definition, function bodies, member initializing list
2017-06-12 09:00:04 +02:00
$ PYTHON_SIGNATURE = detect_and_remove_following_body_or_initializerlist ( ) ;
2017-04-07 11:23:53 +10:00
2017-04-18 09:53:00 +10:00
# remove inline declarations
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^(\s*)?(static |const )*(([\w:]+(<.*?>)?\s+(\*|&)?)?(\w+)( (?:const*?))*)\s*(\{.*\});(\s*\/\/.*)?$/ ) {
2017-04-20 11:18:29 +02:00
my $ newline = "$1$3;" ;
2017-05-29 11:26:44 +02:00
$ LINE = $ newline ;
2017-04-18 09:53:00 +10:00
}
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*(?:const |virtual |static |inline )*(?!explicit)([\w:]+(?:<.*?>)?)\s+(?:\*|&)?(?:\w+|operator.{1,2})\(.*$/ ) {
2017-04-07 11:23:53 +10:00
if ( $ 1 !~ m/(void|SIP_PYOBJECT|operator|return|QFlag)/ ) {
2017-05-29 11:26:44 +02:00
$ RETURN_TYPE = $ 1 ;
2017-04-07 11:23:53 +10:00
# replace :: with . (changes c++ style namespace/class directives to Python style)
2017-05-29 11:26:44 +02:00
$ RETURN_TYPE =~ s/::/./g ;
2017-04-07 11:23:53 +10:00
# replace with builtin Python types
2017-05-29 11:26:44 +02:00
$ RETURN_TYPE =~ s/\bdouble\b/float/ ;
$ RETURN_TYPE =~ s/\bQString\b/str/ ;
$ RETURN_TYPE =~ s/\bQStringList\b/list of str/ ;
if ( $ RETURN_TYPE =~ m/^(?:QList|QVector)<\s*(.*?)[\s*\*]*>$/ ) {
$ RETURN_TYPE = "list of $1" ;
2017-04-07 11:23:53 +10:00
}
2017-05-29 11:26:44 +02:00
if ( $ RETURN_TYPE =~ m/^QSet<\s*(.*?)[\s*\*]*>$/ ) {
$ RETURN_TYPE = "set of $1" ;
2017-04-07 11:23:53 +10:00
}
}
}
2017-03-27 09:27:12 +02:00
} ;
# deleted functions
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^(\s*)?(const )?(virtual |static )?((\w+(<.*?>)?\s+(\*|&)?)?(\w+|operator.{1,2})\(.*?(\(.*\))*.*\)( const)?)\s*= delete;(\s*\/\/.*)?$/ ) {
$ COMMENT = '' ;
2017-03-29 16:15:07 +02:00
next ;
2017-03-27 09:27:12 +02:00
}
2017-04-24 11:50:46 +02:00
# remove export macro from struct definition
2017-05-29 11:26:44 +02:00
$ LINE =~ s/^(\s*struct )\w+_EXPORT (.+)$/$1$2/ ;
2017-04-24 11:50:46 +02:00
2018-01-15 21:09:07 -04:00
# Skip comments
2018-01-16 11:21:13 -04:00
$ COMMENT_TEMPLATE_DOCSTRING = 0 ;
if ( $ LINE =~ m/^\s*typedef\s+\w+\s*<\s*\w+\s*>\s+\w+\s+.*SIP_DOC_TEMPLATE/ ) {
2018-01-15 21:09:07 -04:00
# support Docstring for template based classes in SIP 4.19.7+
2018-01-16 11:21:13 -04:00
$ COMMENT_TEMPLATE_DOCSTRING = 1 ;
2018-01-15 21:09:07 -04:00
}
elsif ( $ LINE =~ m/\/\// ||
$ LINE =~ m/^\s*typedef / ||
$ LINE =~ m/\s*struct / ||
$ LINE =~ m/operator\[\]\(/ ||
$ LINE =~ m/^\s*operator\b/ ||
$ LINE =~ m/operator\s?[!+-=*\/\[\]<>]{1,2}/ ||
$ LINE =~ m/^\s*%\w+(.*)?$/ ||
$ LINE =~ m/^\s*namespace\s+\w+/ ||
$ LINE =~ m/^\s*(virtual\s*)?~/ ||
detect_non_method_member ( ) == 1 ) {
dbg_info ( 'skipping comment' ) ;
dbg_info ( 'because typedef' ) if ( $ LINE =~ m/\s*typedef.*?(?!SIP_DOC_TEMPLATE)/ ) ;
$ COMMENT = '' ;
$ RETURN_TYPE = '' ;
$ IS_OVERRIDE = 0 ;
}
2017-06-02 12:03:19 +02:00
$ LINE = fix_annotations ( $ LINE ) ;
2017-04-24 08:48:12 +02:00
2017-03-27 09:27:12 +02:00
# fix astyle placing space after % character
2017-05-29 11:26:44 +02:00
$ LINE =~ s/\/\s+GetWrapper\s+\//\/GetWrapper\// ;
2017-03-27 09:27:12 +02:00
2017-05-29 11:26:44 +02:00
write_output ( "NOR" , "$LINE\n" ) ;
2017-06-12 09:00:04 +02:00
if ( $ PYTHON_SIGNATURE ne '' ) {
write_output ( "PSI" , "$PYTHON_SIGNATURE\n" ) ;
}
2017-03-28 09:06:45 +02:00
# multiline definition (parenthesis left open)
2017-06-12 09:00:04 +02:00
if ( $ MULTILINE_DEFINITION != MULTILINE_NO ) {
2017-04-29 16:16:17 +02:00
dbg_info ( "on multiline" ) ;
# https://regex101.com/r/DN01iM/2
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^([^()]+(\((?:[^()]++|(?1))*\)))*[^()]*\)[^()]*$/ ) {
2017-04-29 16:16:17 +02:00
dbg_info ( "ending multiline" ) ;
# remove potential following body
2017-06-12 09:00:04 +02:00
if ( $ MULTILINE_DEFINITION != MULTILINE_CONDITIONAL_STATEMENT && $ LINE !~ m/(\{.*\}|;)\s*(\/\/.*)?$/ ) {
2017-04-29 16:16:17 +02:00
dbg_info ( "remove following body of multiline def" ) ;
2017-05-29 11:26:44 +02:00
my $ last_line = $ LINE ;
2017-06-12 09:00:04 +02:00
$ last_line . = remove_following_body_or_initializerlist ( ) ;
2017-04-29 16:16:17 +02:00
# add missing semi column
2017-05-29 11:26:44 +02:00
my $ dummy = pop ( @ OUTPUT ) ;
write_output ( "MLT" , "$last_line;\n" ) ;
2017-04-29 16:16:17 +02:00
}
2017-06-12 09:00:04 +02:00
$ MULTILINE_DEFINITION = MULTILINE_NO ;
2017-04-29 16:16:17 +02:00
}
else
{
next ;
}
2017-03-28 09:06:45 +02:00
}
2017-05-29 11:26:44 +02:00
elsif ( $ LINE =~ m/^[^()]+\([^()]*([^()]*\([^()]*\)[^()]*)*[^)]*$/ ) {
2017-04-27 10:38:12 +02:00
dbg_info ( "Mulitline detected" ) ;
2017-06-12 09:00:04 +02:00
if ( $ LINE =~ m/^\s*((else )?if|while|for) *\(/ ) {
$ MULTILINE_DEFINITION = MULTILINE_CONDITIONAL_STATEMENT ;
}
else {
$ MULTILINE_DEFINITION = MULTILINE_METHOD ;
}
2017-03-28 09:06:45 +02:00
next ;
}
# write comment
2017-05-29 11:26:44 +02:00
if ( $ LINE =~ m/^\s*$/ )
2017-04-17 10:49:29 +10:00
{
2017-05-29 11:26:44 +02:00
$ IS_OVERRIDE = 0 ;
2017-04-17 10:49:29 +10:00
next ;
}
2017-05-21 20:35:27 +02:00
if ( $ LINE =~ m/^\s*template\s*<.*>/ ) {
2017-05-18 14:16:42 +02:00
# do not comment now for templates, wait for class definition
next ;
}
2018-01-15 21:09:07 -04:00
if ( $ COMMENT !~ m/^\s*$/ || $ RETURN_TYPE ne '' ) {
2017-05-29 11:26:44 +02:00
if ( $ IS_OVERRIDE == 1 && $ COMMENT =~ m/^\s*$/ ) {
2017-04-17 11:03:02 +10:00
# overridden method with no new docs - so don't create a Docstring and use
# parent class Docstring
2017-04-07 11:23:53 +10:00
}
2017-04-17 11:03:02 +10:00
else {
2017-05-01 00:13:26 +02:00
dbg_info ( 'writing comment' ) ;
2017-05-29 11:26:44 +02:00
if ( $ COMMENT !~ m/^\s*$/ ) {
2018-01-16 11:21:13 -04:00
my $ doc_prepend = "" ;
2018-01-16 20:26:41 -04:00
$ doc_prepend = "\@DOCSTRINGSTEMPLATE\@" if $ COMMENT_TEMPLATE_DOCSTRING == 1 ;
2018-01-16 11:21:13 -04:00
write_output ( "CM1" , "$doc_prepend%Docstring\n" ) ;
2017-12-06 14:26:43 -04:00
my @ comment_lines = split /\n/ , $ COMMENT ;
foreach my $ comment_line ( @ comment_lines ) {
2017-12-15 13:54:13 -04:00
# if ( $RETURN_TYPE ne '' && $comment_line =~ m/^\s*\.\. \w/ ){
# # return type must be added before any other paragraph-level markup
# write_output("CM5", ":rtype: $RETURN_TYPE\n\n");
# $RETURN_TYPE = '';
# }
2018-01-16 11:21:13 -04:00
write_output ( "CM2" , "$doc_prepend$comment_line\n" ) ;
2017-12-15 13:54:13 -04:00
# if ( $RETURN_TYPE ne '' && $comment_line =~ m/:return:/ ){
# # return type must be added before any other paragraph-level markup
# write_output("CM5", ":rtype: $RETURN_TYPE\n\n");
# $RETURN_TYPE = '';
# }
2017-12-06 14:26:43 -04:00
}
2018-01-16 11:21:13 -04:00
write_output ( "CM4" , "$doc_prepend%End\n" ) ;
2017-04-17 11:03:02 +10:00
}
2017-12-15 13:54:13 -04:00
# if ( $RETURN_TYPE ne '' ){
# write_output("CM3", "\n:rtype: $RETURN_TYPE\n");
# }
2017-04-07 11:23:53 +10:00
}
2017-05-29 11:26:44 +02:00
$ COMMENT = '' ;
$ RETURN_TYPE = '' ;
$ IS_OVERRIDE = 0 ;
2017-03-28 09:06:45 +02:00
}
2017-06-08 07:28:36 +02:00
else {
$ IS_OVERRIDE = 0 ;
}
2017-03-17 10:47:01 +01:00
}
2017-05-29 11:26:44 +02:00
write_header_footer ( ) ;
2017-04-20 11:18:29 +02:00
2018-05-17 22:54:18 -04:00
if ( $ sip_output ne '' ) {
open ( FH , '>' , $ sip_output ) or die $! ;
print FH join ( '' , @ OUTPUT ) ;
close ( FH ) ;
} else {
print join ( '' , @ OUTPUT ) ;
}