#! /usr/bin/perl -w use File::Path; %non_basetype = (); %non_basename = (); %redeclare = (); %strunames = (); %functions = (); %opt = ("save" => 1 ); %shortopt = ( "-v" => "verbose", "-q" => "quiet", "-m" => "methods", "-fk" => "functions"); for (values %shortopt) { $opt{$_} = 0; } @argv_files = (); %argv_outfiles = (); $opt{"outsub"} = "cc"; $_ = $0; if (/\//) { s/\/[^\/]*//; $opt{"basepath"} = $_; } else { $opt{"basepath"} = "."; } # scanning the arguments for (@ARGV) { if (exists $shortopt{$_}) { $opt{$shortopt{$_}} = 1; next; } if (/^--(\w.*)=(.*)$/) { $opt{$1} = $2; next; } # options with args are longoptions if (/^--not?-(\w.*)$/) { $opt{$1} = 0; next; } if (/^--(\w.*)$/) { $opt{$1} = 1; next; } # anything else is a filename of course if (! -f "$_") { print STDERR "$_: file does not exist (skipped)\n"; next; } else { # a filename it should be my $v = outfile_name($_, $opt{"outsub"}); print "[<$_>:<$v>]" if $opt{"outfiles"}; if (exists $argv_outfiles{$v}) { print STDERR "$_: its output $v is also an input file (skipped)\n"; next; } push @argv_files, $_; $argv_outfiles{$_} = $v; } } do $opt{"basepath"}."/"."tokenccode.pl"; $addB = ""; $addE = ""; $used_inherits = 0; for $F (@argv_files) { my $dir; my $i = 100; my $j; my %tokens = (); open F, "<$F" or print STDERR "$F: input skipped: $!" and next; $T = join( "",); close F; &token_c_simplify($T, \%tokens, $i); debug_mksaveof("simps",$T,"$F.simplified"); &token_c_special_clinkage($T, \%tokens, $i); debug_mksaveof("simps",$T,"$F.clinkage"); &token_c_special_flattens($T, \%tokens, $i); debug_mksaveof("simps",$T,"$F.halfflat"); &token_c_special_fullflattens($T, \%tokens, $i); debug_mksaveof("simps",$T,"$F.fullflat"); stage_debug_origs: if ($opt{"origs"}) { debug_mksaveof("origs",$T,"$F.orig-zz"); debug_write_hash("origs",\%tokens,"$F.orig-tk"); my $TT = $T; # check to see that it is reversible so far... &token_c_special_untokenizes($TT, \%tokens); &token_c_unsimplify($TT, \%tokens); debug_mksaveof("origs",$TT,"$F.orig"); } stage_extras: print $F,": " unless $opt{"quiet"}; my %extra = ( "before"=>"", "attach"=>"", "after"=>"", "addB"=>"", "addE"=>"", "use"=>""); set_extras_via($F, \%extra); $addB = $extra{"addB"}; $addE = $extra{"addE"}; %redeclare = (); %non_basename = (); %non_basetype = (); if (length $extra{"use"}) { find_var_redeclare($extra{"use"}, \%redeclare); find_var_non_basename($extra{"use"}, \%non_basename); find_var_non_basetype($extra{"use"}, \%non_basetype); } stage_changes: %s_structs = (); # structname -> bodytoken %strunames = (); # gname -> structname %functions = (); # functionname -> prototypestring %s_basetype = (); # structname -> basetype register_functions($T,\%tokens,\%functions); register_structs($T,\%tokens,\%s_structs); buildof_g_name_of_structs(\%s_structs,\%tokens,\%strunames); buildto_basetype_of_structs(\%s_structs,\%tokens,\%s_basetype); stage_add_basetypes: $used_inherits = 0; kill_non_basetypes(\%s_basetype, \%tokens, \%non_basetype); my $stru; for $stru (keys %s_structs) { my $base = $s_basetype{$stru}; if (defined $base) { $used_inherits +=( $tokens{$s_structs{$stru}} =~ s/ (\{) (\s*(?:\@\wcomment\d+\@\s*)?) (\b\w+\b) ([\w\s]+) (\;) / " __inherits1($base)\n$1 __inherits2($3,$4)$2"/gsex ) } } $inherits_def = "\n#[ifndef __inherits1]" ."\n#[ifndef __cplusplus]" ."\n#[define __inherits1(x)]" ."\n#[define __inherits2(x,y) x y ; ]" ."\n#[else]" ."\n#[define __inherits1(x) : x ]" ."\n#[define __inherits2(x,y)]" ."\n#[endif]" ."\n#[endif]"; $inherits_def = "" if ! $used_inherits; stage_combine_source: my $R; for $R (&find_start_include_files( $extra{"before"},$extra{"attach"},$extra{"after"},$addB,$addE)) { open R,"<$R" or print "$R: combine skipped: $!" and next; my $S = join("",); close R; print "[combine: $R]" if $opt{"debug-combines"}; token_c_special_fullsimplify($S,\%tokens,$i); register_functions($S,\%tokens,\%functions); } print join (" ", keys %strunames)."... " unless $opt{"quiet"}; print join (" ", keys %functions) if $opt{"verbose"}; stage_add_per_struct: for $stru (keys %strunames) { $tokens{$s_structs{$strunames{$stru}}} =~ s/ (\}) / &add_inlines($stru).$1 /gsex; } stage_add_per_file: $addB = "\n\n\#\[ifdef __cplusplus\] \/*generated*\/\n" .$addB ."\n\#\[endif \/*__cplusplus generated *\/\]\n" if $addB !~ /^\s*$/s; $addE = "\n\n\#\[ifdef __cplusplus\] \/*generated*\/\n" .$addE ."\n\#\[endif \/*__cplusplus generated*\/\]\n" if $addE !~ /^\s*$/s; $T =~ s/ (\@Bclinkage\d+\@) / $extra{"before"}.$1.$addB.$inherits_def /sex; $T =~ s/ (\@Eclinkage\d+\@) / $extra{"attach"}.$1.$extra{"after"}.$addE /sex; stage_include_cc_filnames: $T =~ s/ (\#\[\s*include\s) ([^]]*) (\]) / $1.&replace_includearg($2,\%tokens).$3 /gmex; stage_debug_tokens: if ($opt{"tokens"}) { debug_mksaveof("tokens",$T,"$F.zz"); debug_write_hash("tokens",\%tokens,"$F.tk"); } stage_spitout: print "\n" unless $opt{"quiet"}; &token_c_special_untokenizes($T, \%tokens); &token_c_unsimplify($T, \%tokens); if ($opt{"save"}) { $E = $argv_outfiles{$F}; mksaveof($T,$E) or print STDERR "$E: save failed: $!" and next; } } sub outfile_name { # $filename, $outsub -> canonic outfilename my $v = $_[0]; if ($v =~ /\//) { $v =~ s/ (\/) ([^\/]*) $/ $1.$_[1]."\/".$2 /sex; return $v; } else { return $_[1]."\/".$v; } } sub replace_includearg { # includearg, \%tokens my $v = token_untokenize($_[0], $_[1]); $v =~ s/ ([\<\"]) ([^\"\>]+) ([\"\>]) / $1.&replace_include($2).$3 /sex; print $1 if defined $opt{"debug-include-replace"}; return $v; } sub replace_include { # filename -> tested outfilename my $file = $_[0]; return $argv_outfiles{$file} if exists $argv_outfiles{$file}; my $outfile = outfile_name($file, $opt{"outsub"}); return $outfile if (-f $outfile); return $file; } # stores block-tokens with each struct-name in %structs sub register_structs { # $text, \%token, \%structs $_[0] =~ s/ (struct\s+)(\w+) (\s*(?:\@\wcomment\d+\@\s*)?) (\@Sblock\d+\@) / ®ister_structs_1($_[2],$_[1],$1,$2,$3,$4) /gsex; } sub register_structs_1 { # \%structs, \%tokens, $struP, $struD, $blockP, $blockD my ($struP,$struD,$blockP,$blockD) = @_[2..5]; my $struname; ${$_[0]}{$struD} = $blockD if exists ${$_[1]}{$blockD}; return $struP.$struD.$blockP.$blockD; } sub g_name_of { # $name -> $g_name my $g = $_[0]; $g =~ s/ ^_?([A-Z]) / lc($1) /ex; $g =~ s/ ([A-Z]+) / "_".lc($1) /gex; $g =~ s/^_+//; return $g; } # the values of %gnames are the keys of %structs sub buildof_g_name_of_structs { # \%structs, \%dummy, \%strunames my ($i,$n); foreach $i (keys %{$_[0]}) { $n = g_name_of($i); ${$_[2]}{$n} = $i if $n ne $i and $i =~ /_/; } } # the keys of %basetypes are the keys of %structs sub buildto_basetype_of_structs { # \%structs, \%tokens, \%basetypes my ($i,$block); foreach $i (keys %{$_[0]}) { $block = ${$_[1]}{ ${$_[0]}{$i} }; if ( $block =~ m/\{\s* (?:\@\wcomment\d+\@\s*)? (\w+) \s*(?:\@\wcomment\d+\@\s*)? (\w+) \s*(?:\@\wcomment\d+\@\s*)? \;/sx ) { ${$_[2]}{$i} = $1; } } } # undef all entries in basetypes matching the criteria... sub kill_non_basetypes { # \%basetypes, \%dummy, \%non_basetype my ($i,$base); foreach $i (keys %{$_[0]}) { $base = ${$_[0]}{$i}; if (defined $_[2] and exists ${$_[2]}{$base} or $base =~ m/^[a-z]+[1-9]*$/ or $base =~ m/Type$/ or $base =~ m/_type$/ or $base =~ m/Function$/ or $base =~ m/_function$/ ) { ${$_[0]}{$i} = undef; } } } sub register_functions { # $text, \%tokens, \%functions print "[functions: " if $opt{"functions"}; $_[0] =~ s/([\w\s\*]+) (\b\w+) (\s*\(\s*) ([\s\w\[\]\*\,]+) (\s*\)\s*) (\;|\@Pblock\@) / ®ister_functions_1($_[2], $1,$2,$3,$4,$5,$6) /gsex; print "]" if $opt{"functions"}; } sub register_functions_1 { # \%functions, $/m/... my ($rtype,$name,$lbrc,$args,$rbrc,$semi) = @_[1..6]; my $line = join ("",@_[1..6]); print " $name" if $opt{"functions"}; ${$_[0]}{$name} = $rtype.$name.$lbrc.$args.$rbrc; return $line; } # here comes the real magic... sub add_inlines { # struname my $stru = $_[0]; my $add = ""; print " [INLINES for $stru: " if $opt{"verbose"}; for $f (sort keys %functions) { if ($f =~ m/^${stru}_(.*)/ or defined $redeclare{$stru} and $f =~ m/^$redeclare{$stru}_(.*)/) { print $1," " if $opt{"verbose"}; $shortname = $1; $longname = $f; $struct_name = "struct $strunames{$stru}"; $funcdef = $functions{$f}; $rettype = $funcdef; $rettype =~ s/ ^ ([^\(]+) \(.* / $1 /sx; $rettype =~ s/\s+/ /gs; $rettype =~ s/\s$//; $rettype =~ s/\w+$//; $rettype =~ s/^\s//; $rettype =~ s/\s+$//; $funcdecl = $funcdef; $funcdecl =~ s/ ^ [^\(]+ \( / \( /x; $funcargs = $funcdecl; $funcargs =~ s/ ([\w\[\]\*\s]+) (\s|\*)(\w+\s*) (?:\[\d*\]\s*)? ([\)\,]) / $3$4 /gsx; $funcargs =~ s/void//g; $funcdecl =~ s/\s+/ /g; $funcdecl =~ s/^\s//; $funcdecl =~ s/\s$//; $funcargs =~ s/\s+/ /g; $funcargs =~ s/^\s//; $funcargs =~ s/\s$//; $firsttype = $funcdecl; $firsttype =~ s/\s*\(\s*(\w+)(\s|\*).*/$1/; # the GTK people are severely misguided on returning Widget from any new-function $retconv = ""; if ($shortname =~ m/^new$/ or $shortname =~ m/^new_/) { $rettype = "$strunames{$stru}*"; $retconv = "($rettype)"; } $add .= " inline static $rettype _${shortname} $funcdecl; \n"; if ($rettype eq "void") { $addE .= "inline $rettype\n" ."$strunames{$stru}::_${shortname} $funcdecl \n" ." { $longname$funcargs; } \n"; }else{ $addE .= "inline $rettype\n" ."$strunames{$stru}::_${shortname} $funcdecl \n" ." { return $retconv$longname$funcargs; } \n"; } if ($strunames{$stru} eq "_$firsttype" || $struct_name eq $firsttype) { # note: the out-commented version is short in sourcecode but the # c++ compiler will generate bigger codepieces. # (g++ -fenum-int-equiv -fno-rtti -fno-exceptions) $funcdecl =~ s/ (\() [^\,\)]+ ([\,\)]) /$1.($2 eq ")"?")":"")/sex; $funcargs =~ s/ (\() [^\,\)]+ ([\,\)]) /$1."this".$2/sex; $funcdecl = "(void)" if $funcdecl =~ m/\s*\(\s*\)\s*/; if ($rettype eq "void") { # compute the type and name of the second(!) argument if ( $funcargs =~ m/,/) { $funcdecl =~ m/ \s*\( ([^\,\)]+) [\,\)] /sx; $arg2 = $1; $arg2 =~ m/^(.*[\s\*])(\w+)\s*$/s; $rettype = $1; $retconv = "($2)"; $rettype =~ s/^\s+//; # $add .= " inline $rettype ${shortname}_ $funcdecl \n" # ." { _$shortname$funcargs; return $retconv; } \n"; $addE .= "inline $rettype\n" ."$strunames{$stru}::${shortname}_ $funcdecl \n" ." { $longname$funcargs; return $retconv; } \n"; }else{ # $add .= " inline $rettype ${shortname}_ $funcdecl \n" # ." { _$shortname$funcargs; } \n"; $addE .= "inline $rettype\n" ."$strunames{$stru}::${shortname}_ $funcdecl \n" ." { $longname$funcargs; } \n"; } }else{ # $add .= " inline $rettype ${shortname}_ $funcdecl \n" # ." { return $retconv _$shortname$funcargs; } \n"; $addE .= "inline $rettype\n" ."$strunames{$stru}::${shortname}_ $funcdecl \n" ." { return $retconv $longname$funcargs; } \n"; } $add .= " inline $rettype ${shortname}_ $funcdecl; \n"; print $shortname." " if $opt{"methods"}; } } # if startsof like $stru } # for $f (%functions) print "]\n" if $opt{"verbose"}; if (length $add > 1) { return "\n\#\[ifdef __cplusplus\]\n".$add."\n\#\[endif /*__cplusplus*/\]\n"; }else{ return ""; } } sub mkpathof { # filename my $f = $_[0]; return if not $f =~ /\//; $f =~ s/\/[^\/]*$//; mkpath $f if ! -d $f; } sub set_extras_via { # $file, \%extras my $F = $opt{"basepath"}."/extra/".$_[0]; if (-f $F) { open F,"<$F" or print "$F: extra skipped: $!" and return; my $T = join("",); close F; $T =~ s/ \/\*\{\s* (\w+) \s*\*\/ (.*) \/\*\}\s* \1 \s*\*\/ / ${$_[1]}{$1} = $2; "\/*$1*\/" /gsex; } } # will only find the first include file in the textpieces given, # it won't even find an includefile being given after a comment. sub find_start_include_files { # @textpieces my @files = (); my $ff; for $ff (@_) { if (defined $ff and $ff =~ m/\s*\#include\s* [\"\<] ([^\"\>]+) [\>\"] /sx) { push @files, $1; } } return @files; } sub find_var_redeclare { # $text, \%hash while ( $_[0] =~ s/ (\/\* \s* REDECLARE \s* \*\/ \s+) \#define \s+ (\w+) \s+ (\w+) \b / ${$_[1]}{$2} = $3; $1 /gsex) {} $_[0] =~ s/ \/\* \s* REDECLARE \s* \*\/ //gsx; } sub find_var_non_basename { # $text, \%hash while ( $_[0] =~ s/ (\/\* \s* NON?\s*BASENAME \s* \*\/ \s+) \#define \s+ (\w+) \s+ (\w+) \b / ${$_[1]}{$2} = $3; $1 /gsex) {} $_[0] =~ s/ \/\* \s* NON?\s*BASENAME \s* \*\/ //gsx; } sub find_var_non_basetype { # $text, \%hash while ( $_[0] =~ s/ (\/\* \s* NON?\s*BASETYPE \s* \*\/ \s+) \#define \s+ (\w+) \s+ (\w+) \b / ${$_[1]}{$2} = $3; $1 /gsex) {} $_[0] =~ s/ \/\* \s* NON?\s*BASETYPE \s* \*\/ //gsx; } sub mksaveof { # $test, $filename my $F = $_[1]; mkpathof($F); open F,">$F" or print "<$F: $!>" and return 0; print F $_[0]; close F; return 1; } sub debugdirof { # $optionname my $dir = $_[0]; $dir = "debug" if not defined $dir; $dir = $opt{$dir} if defined $opt{$dir}; $dir = "debug" if not defined $dir or $dir == 1; $dir .= "/"; return $dir } sub debug_mksaveof { # optionname, $text, $filename return if not $opt{$_[0]}; $dir = debugdirof($_[0]); mksaveof($_[1],"$dir$_[2]") or print "$dir$_[2]: $_[0] : $!"; } sub debug_write_hash { # optionname, \%tokens, $filename return if not $opt{$_[0]}; $dir = debugdirof($_[0]); token_write_hash("$dir$_[2]",$_[1]) or print "$dir$_[2]: $_[0] : $!"; }