I hope this will be of use to someone. (At least it already helped find
some bugs in tm.texi.)

Usage: Run the script on a mainline GCC source directory. You'll get two
comment-only files, MACHINE.c and MACHINE.h. When you've converted these
into an actual working port, please eliminate *ALL* of the template
comments before submitting. (Quite apart from them being unwanted by the
GCC team [and being butt-ugly, too], they also are a derived work from
both GPL code and GFDL documentation, and as such illegal to distribute,
as far as I can tell.  So, just be a good boy or girl and only use them
internally while developing that port.)

I just threw this script together; no doubt it can be improved quite a
bit. And I expect some of its analyses aren't exactly correct, probably
in most cases because the script still lacks any form of comment
handling, but also because my understanding of GCC still has a lot of
holes.

If you break the script, you get to keep all the pieces, but if you
improve it, I'd like to hear about it.

And here's mktmpl.pl:

#! /usr/bin/perl -w

########################################################################
##
## File:   mktmpl.pl
## Author: Kai Henningsen
## Date:   2007-08-27
##
## Contents:
##   Script to create template files for MACHINE.h and MACHINE.c
##   by analyzing tm.texi and source files
##
## Copyright (c) 2007 Kai Henningsen
##
## mktmpl.pl is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2, or (at your option)
## any later version.
##
## mktmpl.pl is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You can find a copy of the GPL at
## http://www.gnu.org/licenses/old-licenses/gpl-2.0.html, or write to
## the Free Software Foundation, 51 Franklin Street, Fifth Floor,
## Boston, MA 02110-1301, USA.
##
#########################################################################

use strict;
use Data::Dumper::Simple; $Data::Dumper::Useqq = 1;
use File::Find;

# call it as
#       mktmpl.pl gccsrcdir
chdir $ARGV[0] or die "Can't chdir to src directory";

# which argument of these macros is the name?
my %nampos = (
        '@defmac'       => 0,
        '@defmacx'      => 0,
        '@deftp'        => 1,
        '@deftypefn'    => 2,
        '@deftypefun'   => 1,
        '@deftypefunx'  => 1,
        '@deftypevar'   => 1,
        '@deftypevr'    => 2,
        '@deftypevrx'   => 2,
);

# some filenames
my $tm = "gcc/doc/tm.texi";
my ($mh, $mc) = qw( ../MACHINE.h ../MACHINE.c );
# ... and something that's not a name
use constant none => 'not a def';

my %tmpl;       # text for the templates
my @seq;        # the sequence of tm.texi entries, so we can preserve it

# scan through tm.texi
my $cur = none();
open TM, '<', $tm or die "$tm: $!";
while (<TM>) {
        push @{$tmpl{$cur}}, " $_" unless /[EMAIL PROTECTED]/;
        push @{$tmpl{$cur}}, "\n" if /[EMAIL PROTECTED] def/;
        $cur = none() if /[EMAIL PROTECTED] def/;
        /[EMAIL PROTECTED]/ or next;
        # ok, we have a definition line
        my $l = $_;
        # handle continuation lines
        while ($l =~ /[EMAIL PROTECTED]/) {
                $l =~ s/[EMAIL PROTECTED]//s;
                $l .= <TM>;
        }
        chomp $l;
        # split into arguments
        my @l = grep /\S/, split /(\s+|{[^}]*}|(?!{)\S+)/, $l;
        my $d = shift @l;
        my $n = $nampos{$d};
        warn Dumper(@l) unless defined $n;
        die "Unknown [EMAIL PROTECTED]: «$d»" unless defined $n;
        my $name = $l[$n];
        my $orgname = $name;
        # fix various problems in tm.texi as of rev. 127318
        $name =~ s/^\*+//;
        $name =~ s/^{(.*)}$/$1/;
        $name = $l[$n-1] if $name eq '(void)';
        $name = $l[$n+1] if $name eq 'HOST_WIDE_INT';
        $name = $l[$n+1] if $name eq 'bool';
        $name = $l[$n+1] if $name eq 'char';
        $name = $l[$n+1] if $name eq 'int';
        $name = $l[$n+1] if $name eq 'machine_mode';
        $name = $l[$n+2] if $name eq '*';
        $name =~ s/^\*+//;
        $name =~ s/^{(.*)}$/$1/;
        # if it's a @defqqqqx, note that the previous one is continued
        push @{$tmpl{$cur}}, "see $name\n\n" if $d =~ /x$/;
        unless ($name eq $orgname) {
                warn "\n", Dumper @l, $name, $orgname, $l;
        }
        # enum reg_class
        $name =~ s/^enum\s+//;
        # SDB_PUT_...
        $name =~ s/[EMAIL PROTECTED]/*/;
        die "split name: «$name»" if $name =~ /\s/;
        $cur = $name;
        push @seq, $name;
        push @{$tmpl{$cur}}, " $l\n";
}
close TM or die "$tm: $!";
delete $tmpl{none()};

my (%def, %ifdef, %defaults, %target_def, %seen_in);

# scan most of the sources
find(\&wanted, '.');

# possible candidate line, with matching ids
sub foundone
{
        my ($fn, $fname, $ln, $s, $var) = @_[0..4];
        @_ = @_[5..$#_];
        #return unless @_;
        #warn Dumper @_;
        for my $word (@_) {
                if (exists $tmpl{$word}) {
                        $$var{$word}++;
                        $defaults{$word}++ if $fn eq 'defaults.h';
                        $target_def{$word}++ if $fn eq 'target-def.h';
                        push @{$tmpl{$word}}, "${fname}:$.:$s\n";
                }
                $word = substr($word, 0, 8).'*';        # SDB_PUT_...
                if (exists $tmpl{$word}) {
                        $$var{$word}++;
                        $defaults{$word}++ if $fn eq 'defaults.h';
                        $target_def{$word}++ if $fn eq 'target-def.h';
                        push @{$tmpl{$word}}, "${fname}:$.:$s\n";
                }
        }
}

# process one file
sub wanted
{
        my $fn = $_;
        # avois these directories
        if ($fn eq 'config' or $fn eq 'po' or $fn eq '.svn' or $fn eq 
'testsuite') {
                $File::Find::prune = 1;
                return;
        }
        # ... and these files (and any non-files)
        return if $fn =~ /changelog/i or $fn =~ /\.texi$/ or not -f $fn;
        my $fname = $File::Find::name;
        $fname =~ s[^\./][];    # cosmetics
        # ok, read this source file
        open SRC, '<', $fn or die "$fname: $!";
        my $s;
        while (defined($s = <SRC>)) {
                # continuation lines
                while ($s =~ /\\\s*$/) {
                        $s =~ s/\\\s*$//s;
                        $s .= <SRC>;
                }
                chomp $s;
                # find all identifiers FIXME: should handle comments
                my @words = grep /\w/, split /(\w+)/, $s;
                #print Dumper($., $s, @words);
                # ... and note where we've seen them, if they are interesting
                for my $word (@words) {
                        $seen_in{$word}{$fname}++ if exists $tmpl{$word};
                        $word = substr($word, 0, 8).'*';        # SDB_PUT_...
                        $seen_in{$word}{$fname}++ if exists $tmpl{$word};
                }
                # ok, next look at all preprocessor lines that are
                # concerned with the existence of a macro
                # and note some of the more interesting ones
                next unless $s =~ /^\s*#/;
                $s =~ s/\s+/ /g;
                foundone $fn, $fname, $., $s, \%def, $s =~ 
/^\s*#\s*define\s+(\w+)\b/;
                foundone $fn, $fname, $., $s, {}, $s =~ 
/^\s*#\s*undef\s+(\w+)\b/;
                foundone $fn, $fname, $., $s, \%ifdef, $s =~ 
/^\s*#\s*ifn?def\s+(\w+)\b/;
                foundone $fn, $fname, $., $s, \%ifdef, $s =~ 
/^\s*#\s*if\b.*\bdefined\s*\(\s*(\w+)\s*\)/;
        }
        close SRC or die "$fname: $!";
}

# OK, we can start to write the two files
open MH, '>', $mh or die "$mh: $!";
open MC, '>', $mc or die "$mc: $!";

# pretty printer
sub printcomment
{
        my $fh = shift;
        my $line = join('', @_);
        $line =~ s[/\*][/ *]g;
        $line =~ s[\*/][* /]g;
        printf $fh "/*%-*s*/\n", 76, $line;
}

#print OUT Dumper %seen_in;
for my $name (@seq) {
        my $hc = undef;
        my $which = undef;
        # lower case stuff is special
        if ($name =~ /[A-Z]/) {
                # make sure we have the right data types
                $seen_in{$name}{" "}++;
                delete $seen_in{$name}{" "};
                # if we saw this name in anything but a header,
                # it needs to go in the .h
                my %si = %{$seen_in{$name}};
                #delete $si{'gcc/target-def.h'};
                my @si = grep !/\.h$/, keys %si;
                #printcomment Dumper(@si);
                $hc = ((scalar @si)? *MH: *MC);
                $which = ((scalar @si)? 'h': 'c');
        }
        else {
                if ($name eq 'targetm') {
                        $hc = *MC;
                        $which = 'c';
                }
                elsif ($name eq 'reg_class') {
                        $hc = *MH;
                        $which = 'h';
                }
                else {
                        $hc = *MC;
                        $which = 'e';
                }
        }

        printcomment $hc, '-' x 76;
        printcomment $hc, $name, " (", keys %{$seen_in{$name}}? ($which eq 'c'? 
"belongs in MACHINE.c": $which eq 'h'? "belongs in MACHINE.h": "defined 
elsewhere"): "completely unused", ")";
        if ($name =~ /[A-Z]/) {
                printcomment $hc, $ifdef{$name}? "may be un": "must be ", 
"defined";
                printcomment $hc, $def{$name}? "has": "no", " default";
        }
        printcomment $hc, "defaults.h" if $defaults{$name};
        printcomment $hc, "target-def.h" if $target_def{$name};
        printcomment $hc, "Seen in ", join(' ', sort keys %{$seen_in{$name}});
        for my $line (split(/\n/, join('', @{$tmpl{$name}}))) {
                printcomment $hc, $line;
        }
        printcomment $hc, '-' x 76;
        print $hc "\n";
}
close MH or die "$mh: $!";
close MC or die "$mc: $!";

Reply via email to