Hi, I got annoyed with enum.pl generating broken modulemm_enums.defs and in effect wrapped flags have often wrong values, but I did not feel to be able to bake some completely new solution. So I rewritten most of the original script.
I updated it to work with `use strict' and `use warnings', added --with-typedefs (prints original enums in comments above every enum defs) and --omit-deprecated (just omits everything deprecated) options, handles most of enums and flags (especially flags!) properly, generates proper nicks for enums with one value, prints warnings to stderr about several stuff. It is a better hack, but still a hack. I'm attaching a patch made against master branch of glibmm. Do I have to file a bug in bugzilla? (In which category?) I tested it a bit on master branches of glib (glib, gobject, gio), pango, atk, gtk (gdk, gdk-pixbuf, gtk), vte and gtksourceview, but that is so much of enums and flags that I could easily overlook something. Krzesimir.
>From 1a34a37c660b2d799061909968815689f2fd5731 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak <[email protected]> Date: Sun, 13 Dec 2009 20:14:21 +0100 Subject: [PATCH] Updated enum.pl script. * tools/enum.pl: Updated to work with `use strict' and `use warnings', added --with-typedefs (prints original enums in comments above every enum defs) and --omit-deprecated (just omits everything deprecated) options, handles most of enums and flags (especially flags!) properly, generates proper nicks for enums with one value, prints warnings to stderr about several stuff. It is a better hack, but still a hack. --- tools/enum.pl | 455 ++++++++++++++++++++++++++++++++++++++------------------ 1 files changed, 309 insertions(+), 146 deletions(-) diff --git a/tools/enum.pl b/tools/enum.pl index 357931d..34f8d74 100755 --- a/tools/enum.pl +++ b/tools/enum.pl @@ -3,230 +3,393 @@ # The lisp definitions for flags does not include order. # thus we must extract it ourselves. # Usage: ./enum.pl /gnome/head/cvs/gconf/gconf/*.h > gconf_enums.defs - use warnings; +use strict; use File::Spec; - -my %token; -$module="none"; - -while ($ARGV[0] =~ /^--(\S+)/) +use Getopt::Long; +use IO::File; +# +# globals. +# +# keeps enum values. +my %tokens = (); +# module name. +my $module = "none"; +# if user used --help option. +my $help = 0; +# if user wants to have also original typedefs printed as a comments. +my $typedefs = 0; +# if user wants to omit deprecated stuff. +my $omit = 0; +# +# prototypes. +# +sub parse($); +sub process($$); +sub form_names($$); +# +# main. +# +GetOptions('module=s' => \$module, 'help' => \$help, 'with-typedefs' => \$typedefs, 'omit-deprecated'=> \$omit); +if ($help or not @ARGV) { - shift @ARGV; - $module=shift @ARGV if ($1 eq "module"); - if ($1 eq "help") - { - print "enum.pl [--module modname] header_files ....\n"; - exit 0; - } + print "enum.pl [--module modname][--with-typedefs][--omit-deprecated] header_files ...\n"; + exit 0; } - -foreach $file (@ARGV) +foreach my $file (@ARGV) { - &parse($file); + parse($file); } - exit; - -# parse enums from C -sub parse ($) +# +# parse enums from C. +# +sub parse($) { - my ($file)=...@_; - - $from=0; - open(FILE,$file); - - $enum=0; - $deprecated=0; - $comment=0; - - while(<FILE>) + my ($file) = @_; + my $fd = IO::File->new($file, "r"); + unless (defined $fd) { + print STDERR "WARNING: Unable to open file: '" . $file . "'.\n"; + return; + } + # 1, if we are inside enum. + my $enum = 0; + # 1 or more, if we are inside deprecated lines. + my $deprecated = 0; + # 1, if we are inside multiline comment. + my $comment = 0; + # line containing whole enum preprocessed definition to be processed. + my $line = ""; + # line containing whole enum raw definition. + my $raw_line = ""; + # 1, if we already printed comment about basename of header file containing + # enums. + my $from = 0; + while(<$fd>) + { + my $tmp_rawline = $_ if ($typedefs); + if ($enum and $typedefs) + { + $raw_line .= ";; " . $tmp_rawline; + } if($comment) { - # end of multiline comment - $comment = 0 if(m!\*/!); + # end of multiline comment. + if (m!\*/(.*)!) # / just to fix frigging highlighting in gedit + { + $comment = 0; + if ($enum) + { + $line .= $1; + } + } next; } - - $deprecated = 1 if(s/^#ifndef [A-Z_]+_DISABLE_DEPRECATED//); - - ++$deprecated if($deprecated > 0 && /^#\s*if/); - --$deprecated if($deprecated > 0 && /^#\s*endif/); - - next if($deprecated > 0); - - # filter single-line comments + # omit deprecated stuff. + if ($omit and /^\s*#.*(if\s*!\s*defined)|(ifndef)\s*\(?\s*[A-Z_]+_DISABLE_DEPRECATED\s*\)?/) + { + ++$deprecated; + next; + } + ++$deprecated if ($deprecated > 0 and /^#\s*if/); + if ($deprecated > 0 and /^#\s*endif/) + { + --$deprecated; + next; + } + next if ($deprecated > 0); + # discard any preprocessor directives inside enums. + next if ($enum and /^\s*#/); + # filter single-line comments. s!/\*.*?\*/!!g; - - # begin of multiline comment - if(m!/\*!) + s!//.*$!!; + # beginning of multiline comment. + if (m!^(.*)/\*!) { $comment = 1; + if ($enum) + { + $line .= $1 . "\n"; + } next; } - + # XXX: what does it do? s/','/\%\%COMMA\%\%/; s/'}'/\%\%RBRACE\%\%/; + # we have found an enum. if (/^\s*typedef enum/ ) { my $basename = File::Spec->splitpath($file); - print(';; From ', $basename, "\n\n") if (!$from); - $from=1; - $enum=1; + print(';; From ', $basename, "\n\n") unless ($from); + $from = 1; + $enum = 1; + if ($typedefs) + { + $raw_line .= ";; " . $tmp_rawline; + } next; } - + # we have found end of an enum. if ($enum && /\}/) { - $enum=0; - &process($line,$_); - $line=""; + $enum = 0; + if ($typedefs) + { + print ";; Original typedef:\n"; + print $raw_line . "\n"; + } + process($line, $_); + $line = ""; + $raw_line = "" if ($typedefs); } - $line.=$_ if ($enum); + $line .= $_ if ($enum); } + $fd->close(); } -# convert enums to lisp -sub process ($$) +# +# convert enums to lisp. +# +sub process($$) { - my ($line,$def)=...@_; + my ($line,$def) = @_; - $def=~s/\s*\}\s*//g; - $def=~s/\s*;\s*$//; - my $c_name=$def; + $def =~ s/\s*\}\s*//g; + $def =~ s/\s*;\s*$//; + my $c_name = $def; - $line=~s/\s+/ /g; - $line=~s!/\*.*\*/!!g; - $line=~s/\s*{\s*//; + $line =~ s/\s+/ /g; + $line =~ s!/\*.*\*/!!g; + $line =~ s/\s*{\s*//; my $entity = "enum"; $c_name =~ /^([A-Z][a-z]*)/; $module = $1 if ($module eq "none"); $def =~ s/\Q$module\E//; - @c_name=(); - @name=(); - @number=(); + my @c_names = (); + my @numbers = (); - $val=0; - foreach $i (split(/,/,$line)) - { - $i=~s/^\s+//; - $i=~s/\s+$//; - if ($i =~ /^\S+$/) - { - push(@c_name,$i); - push(@number,sprintf("%d",$val)); - $token{$i}=$val; + my $val = 0; + my $unknown_flag = 0; + my $unknown_val = 0; + foreach my $i (split(/,/, $line)) + { + # remove leading and trailing spaces. + $i =~ s/^\s+//; + $i =~ s/\s+$//; + # also remove backslashes as some people like to add them before newlines... + $i =~ s/\\//g; + # if only name exists [like MY_ENUM_VALUE]. + if ($i =~ /^\S+$/) + { + push(@c_names, $i); + if ($unknown_flag) + { + push(@numbers, $unknown_val); + $tokens{$i} = $unknown_val; } - elsif ($i =~ /^(\S+)\s*=\s*(0x[0-9a-fA-F]+)$/ || - $i =~ /^(\S+)\s*=\s*(-?[0-9]+)$/ || - $i =~ /^(\S+)\s*=\s*\(?(1\s*<<\s*[0-9]+)\)?$/ - ) - { - my ($tmp1, $tmp2) = ($1, $2); - push(@c_name, $tmp1); - eval("\$val = $tmp2;"); - $entity = "flags" if($tmp2 =~ /^1\s*<</ || $tmp2 =~ /^0x/); - push(@number, $tmp2); - $token{$tmp1} = $tmp2; + else + { + push(@numbers, sprintf("%d", $val)); + $tokens{$i} = $val; } - elsif ($i =~ /^(\S+)\s*=\s*([ _x0-9a-fA-Z|()~]+)$/) - { - my ($tmp1, $tmp2) = ($1, $2); - push(@c_name, $tmp1); - $tmp2 =~ s/([A-Z_]+)/($token{$1})/; - eval("\$val = $tmp2;"); - $val = "#error" if(!$val); - $val = sprintf("0x%X", $val) if($entity eq "flags"); - push(@number, $val); - $token{$tmp1} = $val; + } + # if name with value exists [like MY_FLAG_VALUE = 0x2 or 0x5 << 22 + # or 42 or -13 (in this case entity is still enum, not flags) + # or 1 << 2 or (1 << 4) or (1 << 5) - 1]. + elsif ($i =~ /^(\S+)\s*=\s*(0x[0-9a-fA-F]+[\s0-9a-fx<-]*)$/ || + $i =~ /^(\S+)\s*=\s*(-?\s*[0-9]+)$/ || + $i =~ /^(\S+)\s*=\s*(\(?1\s*<<\s*[0-9]+\s*\)?[\s0-9a-fx<-]*)$/ + ) + { + my ($tmp1, $tmp2) = ($1, $2); + push(@c_names, $tmp1); + eval("\$val = $tmp2;"); + $entity = "flags" if ($tmp2 =~ /^1\s*<</ || $tmp2 =~ /^0x/); + push(@numbers, $tmp2); + $tokens{$tmp1} = $val; + $unknown_flag = 0; + } + # if name with other name exists [like MY_FLAG_VALUE = MY_PREV_FLAG_VALUE + # or ~(MY_PREV_FLAG_VALUE | MY_EARLIER_VALUE | (1 << 5) - 1 | 0x200)]. + # [MY_FLAG MY_OTHER_FLAG is also supported - note lack of equal char.] + elsif ($i =~ /^(\S+)\s*=?\s*([ _x0-9a-fA-Z|()<~]+)$/) + { + my ($tmp1, $tmp2) = ($1, $2); + push(@c_names, $tmp1); + # split r-values on "logical or" and for each splitted r-value check its + # numeric value and replace a name with it if possible. + my @tmps = split(/\|/, $tmp2); + # dont_eval is 1 if unknown token is found, so whole value is copied + # verbatim, without evaling. + my $dont_eval = 0; + foreach my $tmpval (@tmps) + { + # if r-value is something like MY_FLAG or MY_DEFINE_VALUE3. + if ($tmpval =~ /([_A-Z0-9]+)/) + { + my $tmp3 = $1; + unless (defined($tokens{$tmp3})) + { + $dont_eval = 1; + print STDERR "WARNING: " . $tmp3 . " in " . $tmp1 . " in '" . $c_name . "' is an unknown token. It probably is:\n - a typo - send a patch to maintainer of this module,\n - preprocessor value - make sure you include a header defining this value,\n - enum value from other header or other module - see above.\n"; + } + else + { + $tmp2 =~ s/$tmp3/$tokens{$tmp3}/; + } + } + # else is a numeric value, so we do not do anything. } - elsif ($i =~ /^(\S+)\s*=\s*'(.)'$/) + # check if there are still same non-numerical values. + if ($tmp2 =~ /[_A-Z]+/) { - push(@c_name,$1); - push(@number,"\'$2\'"); - $val=ord($2); - $token{$1}=$val; + $dont_eval = 1; } - elsif ($i =~ /^(\S+)\s*=\s*(\%\%[A-Z]+\%\%)$/) + unless ($dont_eval) { - $tmp=$1; - $_=$2; - s/\%\%COMMA\%\%/,/; - s/\%\%RBRACE\%\%/]/; - push(@c_name,$tmp); - push(@number,"\'$_\'"); - $val=ord($_); - $token{$tmp}=$val; + eval("\$val = $tmp2;"); + $val = sprintf("0x%X", $val) if ($entity eq "flags"); + push(@numbers, $val); + $tokens{$tmp1} = $val; + $unknown_flag = 0; } else { - #print STDERR "$i\n"; + push(@numbers, $tmp2); + $unknown_flag = 1; + $unknown_val = "(" . $tmp2 . ")"; + # wrapping in safety parens. + $tokens{$tmp1} = $unknown_val; } + } + # if name with char exists (like MY_ENUM_VALUE = 'a'). + elsif ($i =~ /^(\S+)\s*=\s*'(.)'$/) + { + push(@c_names, $1); + push(@numbers, "\'$2\'"); + $val = ord($2); + $tokens{$1} = $val; + $unknown_flag = 0; + } + # if... XXX: I do not know what is matched here. + elsif ($i =~ /^(\S+)\s*=\s*(\%\%[A-Z]+\%\%)$/) + { + my $tmp = $1; + $_ = $2; + s/\%\%COMMA\%\%/,/; + s/\%\%RBRACE\%\%/]/; + push(@c_names, $tmp); + push(@numbers, "\'$_\'"); + $val = ord($_); + $tokens{$tmp} = $val; + $unknown_flag = 0; + } + # it should not get here. + else + { + print STDERR "WARNING: I do not know how to parse it: '" . $i . "' in '" . $c_name . "'.\n"; + } + if ($unknown_flag) + { + # wrap in safety parens. + $unknown_val = "(" . $unknown_val . " + 1)"; + } + else + { $val++; } - - # remove the prefix to form names - &form_names(\...@name,\...@c_name); - + } + # get nicks. + my $ref_names = form_names($c_name, \...@c_names); + # set format - decimal for enums, hexadecimal for flags. my $format = "%d"; - $format = "0x%X" if($entity eq "flags"); - - # evaluate any unevaluated values - my $j; - for ($j=0;$j<$#number+1;$j++) + $format = "0x%X" if ($entity eq "flags"); + # evaluate any unevaluated values. + for (my $j = 0; $j < @numbers; $j++) { - if ($number[$j]=~/\$/) + if ($numbers[$j] =~ /\$/) { - $number[$j]=sprintf($format, eval($number[$j])); + $numbers[$j] = sprintf($format, eval($numbers[$j])); } } - - #print ";; Enum $def\n\n"; + # print the defs. print "(define-$entity-extended $def\n"; print " (in-module \"$module\")\n"; print " (c-name \"$c_name\")\n"; print " (values\n"; - for ($j=0;$j<$#c_name+1;$j++) + for (my $j = 0; $j < @c_names; $j++) { - print " \'(\"$name[$j]\" \"$c_name[$j]\""; - print " \"$number[$j]\"" if ($number[$j] ne ""); + print " \'(\"$ref_names->[$j]\" \"$c_names[$j]\""; + print " \"$numbers[$j]\"" if ($numbers[$j] ne ""); print ")\n"; } print " )\n"; print ")\n\n"; } - -sub form_names +# +# form nicks from C names. +# +sub form_names($$) { - my ($name,$c_name)=...@_; - - my $len=length($$c_name[0]) - 1; - my $j; - - NAME: for ($j=0;$j<$#c_name;$j++) + my ($c_name, $c_names) = @_; + my @names = (); + # search for length of a prefix. + my $len = length($c_names->[0]) - 1; + # if there is more than one value in enum, just search for a common part. + if (@{$c_names} > 1) { - while (substr($$c_name[$j],$len-1,1) ne "_" || - substr($$c_name[$j],0,$len) ne substr($$c_name[$j+1],0,$len)) + NAME: for (my $j = 0; $j < @{$c_names} - 1; $j++) + { + while (substr($c_names->[$j], $len - 1, 1) ne "_" || + substr($c_names->[$j], 0, $len) ne substr($c_names->[$j + 1], 0, $len)) + { + $len--; + last NAME if ($len <= 0); + } + } + } + # if there is only one value in enum, we have to use name of the enum. + elsif (@{$c_names} == 1) + { + my @subvals = split(/_/, lc($c_names->[0])); + foreach my $subval (@subvals) + { + $subval = ucfirst($subval); + } + my $false_c_name = join("", @subvals); + while (substr($c_name, 0, $len) ne substr($false_c_name, 0, $len)) { $len--; - last NAME if ($len <= 0); + last if ($len <= 0); + } + my $tmplen = $len; + foreach my $subval (@subvals) + { + $len++; + my $l = length($subval); + last if ($tmplen <= $l); + $tmplen -= $l; } - #print substr($$c_name[$j],0,$len),"\n"; } - - my $prefix=substr($$c_name[0],0,$len); - - for ($j=0;$j<$#c_name+1;$j++) + # no values in enum means no names. + else + { + return \...@names; + } + # get prefix with given length. + my $prefix = substr($c_names->[0], 0, $len); + # generate names. + for (my $j = 0; $j < @{$c_names}; $j++) { - $_=$$c_name[$j]; + $_ = $c_names->[$j]; s/^$prefix//; tr/A-Z_/a-z-/; - push(@$name,$_); + push(@names, $_); } - + return \...@names; } -- 1.6.5.2
_______________________________________________ gtkmm-list mailing list [email protected] http://mail.gnome.org/mailman/listinfo/gtkmm-list
