URL: https://svn.lrde.epita.fr/svn/lrde-tools/trunk
I <3 Perl.
How many years would be required to do this with Transformers? ;)
ChangeLog: 2007-11-28 Benoit Sigoure tsuna@lrde.epita.fr
Add the script `add-inline'. * README: Advertise the script. * src/Makefile.am: Add the script. * src/add-inline: New.
--- README | 5 + src/Makefile.am | 1 src/add-inline | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+)
Index: trunk/src/add-inline =================================================================== --- trunk/src/add-inline (revision 0) +++ trunk/src/add-inline (revision 475) @@ -0,0 +1,278 @@ +#!/usr/bin/env perl +# add-inline.pl: Add missing `inline' keywords in C++ headers. +# Copyright (C) 2007 Benoit Sigoure tsuna@lrde.epita.fr +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +use warnings; +use strict; # spank me baby! + +# The following pattern matches a parenthesized group. +# Modified version of that proposed in man perlre(1). +my $paren_group = qr/defined/; # Fool use strict. +$paren_group = qr/(?: # Non-parens without backtracking: + (?> [^()]+ ) + | # Group with matching parens: + ( (??{ $paren_group }) ) + )* + /xo; + +# Almost the same regexp but matches an entire block of braces { ... } +my $brace_group = qr/defined/; # Fool use strict. +$brace_group = qr/(?: # Non-brace without backtracking: + (?> [^{}]+ ) + | # Group with matching braces: + { (??{ $brace_group }) } + )* + /xo; + +# Exactly the same as braces but for curly brackets (for templates). +my $curly_group = qr/defined/; # Fool use strict. +$curly_group = qr/< + (?: # Non-curly without backtracking: + (?> [^<>]+ ) + | # Group with matching curly brackets: + (??{ $curly_group }) + )* + > + /xo; + +# A C++ comment +# This wonderful RE is explained in perlfaq6. Credits: Fred Curtis. +my $comment = qr< + /*[^*]**+(?:[^/*][^*]**+)*/ + |//[^\n]* + >x; + +# Some common CPP directives +my $cpp_directive = qr{#\s*(?: + define\s+\S+ + |if[^\n]+ + |ifn?def[^\n]+ + |undef + |include [<"].*?[">] + )}x; + +# string: CPP tag to search in the source files. +my $tag = undef; + +# string: name of the file currently processed. +my $file = undef; + +my $backup_suffix = $ENV{SIMPLE_BACKUP_SUFFIX} || '~'; + +# Verbose level (0 = quiet, 1 = normal, 2 = verbose) +my $verbose = 1; + +sub msg +{ + print "@_\n" if $verbose; +} + +sub verbose +{ + &msg if $verbose > 1; +} + +# line_count(STR) +# --------------- +# Count the number of line in STR +sub line_count($) +{ + my $s = shift; + return ($s =~ tr/\n/\n/) + 1; +} + +# add_inline(LINE, BEGIN, TEMPLATE, FN, REST) +# ------------------------------------------- +# Helper function for add_all_inline below. Adds the `inline' keyword where +# needed if needed. The arguments (except LINE) are strings that are matched +# with a pattern commented in the function near the (only) call site of this +# function. +# Returns a couple (changed, string) where changed is TRUE if string has been +# changed compared to the input source (that is, if `inline' was added in +# string). +sub add_inline($$$$$) +{ + my ($line, $begin, $template, $fn, $rest) = @_; + my $pretty_fn = $fn; + my $l = $line + line_count($` . $begin . $template) + 1; + $pretty_fn =~ s/\s+/ /g; + chomp($pretty_fn); + $pretty_fn =~ s/^\s+//; + my $inline = ''; + my $changed = 0; + if ($fn !~ /\binline\b/) + { + msg "$file: Adding inline to ``$pretty_fn'' near line $l"; + # Find the indentation. + $fn =~ /^\s*/; + my $indent = $&; + $inline = $indent . 'inline'; + $changed = 1; + # Re-ident the function-name if we're just about to stick `indent' and + # the function name together. + if ($fn !~ /^\s/s) + { + $indent =~ s/^\s\n*//; + if ($indent eq '') + { + $begin =~ /\s*$/s; + $indent = $&; + $indent =~ s/^\s*\n//; + } + $fn = "\n$indent$fn"; + } + } + return ($changed, $begin . $template . $inline . $fn . $rest); +} + +# add_all_inline(LINENO, CODE) +# ---------------------------- +# Add all the `inline' keywords in CODE that starts at the input line LINEO. +# Returns a couple (changed, string) where `changed' is the number of times +# `inline' was added in `string'. +sub add_all_inline($$) +{ + my ($line, $code) = @_; + my $changed = 0; # Number of times we added `inline'. + # Match the beginning of *definitions*. We locate definitions by matching + # (1) either the start of the #ifndef MLN_INCLUDE_ONLY block or by seeing a + # brace or a semi-colon. Then (2) we need to see whether we have template + # parameters (we most likely do, and we can even have multiple levels of + # template parameters for method templates in class templates). Sometimes + # we can find comments right after the template parameters, so we accept + # them in (3). Then (4) we greedily match the name of the function or + # method. The name is anything but braces and semi-colons. Then (5) we + # have the list of formal arguments followed by (6) an optional `const' + # qualifier. Eventually (7) matches the body of the function. Care is + # taken to not actually match the closing right-brace that terminates the + # function, otherwise the next match can't use this brace to detect the next + # function (things can be matched only once, after they are "consumed"). + $code =~ s{ + ((?:^|[{};]) # (1) + (?:\s|$comment|$cpp_directive)*) + ((?:\s*(?:template\s*$curly_group) # (2) + (?:\s*$comment)?)*) # (3) + ([^;{}]+?) # (4) + (($paren_group) # (5) + (?:\s*const)? # (6) + \s*[{]$brace_group)(?=[}]) # (7) + } + { + my ($n, $c) = add_inline ($line, $1, $2, $3, $4); + $changed += $n; + $c + }gsex; + return ($changed, $code); +} + +sub help($) +{ + my $rv = shift; + my $fd = $rv ? *STDERR : *STDOUT; + # Keep the -t option first (since it's mandatory) + print $fd <<EOF; +Usage: $0 OPTIONS FILES +Add missing `inline' keywords in C++ headers. + +Options: + -t, --tag=string This option is MANDATORY. It specifies the preprocessor + symbol to look for when searching for code blocks where + `inline' must be added. + -q, --quiet Don't print anything. + -s, --suffix=str Simple backup suffix to use (files are always backed up + before changing them). Defaults to `~'. The environment + variable SIMPLE_BACKUP_SUFFIX is honored. + -v, --verbose Be more verbose. + +Copyright (C) 2007 Benoit Sigoure. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +EOF + exit $rv; +} + +# Parse ARGV, return the list of files to update. +sub parse_options() +{ + use Getopt::Long; + Getopt::Long::config ('bundling', 'pass_through'); + Getopt::Long::GetOptions + ( + 'h|help' => sub { help(0); }, + 's|suffix=s' => $backup_suffix, + 't|tag=s' => $tag, + 'v|verbose' => sub { ++$verbose; }, + 'q|quiet' => sub { --$verbose; }, + ) + or die; + + foreach my $arg (@ARGV) + { + if ($arg =~ /^-./) + { + print STDERR "$0: unrecognized option or bad usage for `$arg'\n"; + print STDERR "Try `$0 --help' for more information.\n"; + exit 1; + } + } + return @ARGV; +} + +# process(PATH) +# ----------------- +# Process the file PATH. +sub process($) +{ + $file = shift; + verbose "processing `$file'"; + + open(IN, '<', $file) or die "Failed to open `$file' for reading: $!"; + $_ = join('', <IN>); + close(IN) or die "Failed to close `$file': $!"; + + my $changed = 0; + s{ + (#\s*ifndef\s+\Q$tag\E) + (.*?) + (#\s*endif) + } + { + my ($n, $c) = add_all_inline(line_count($`), $2); + $changed += $n; + $1 . $c . $3 + }sex; + if ($changed) + { + rename($file, $file . $backup_suffix) or die "Failed to backup `$file': $!"; + + open(OUT, '>', $file) or die "Failed to open `$file' for writing: $!"; + print OUT; + close(OUT) or die "Failed to close `$file': $!"; + } + else + { + msg "`$file' is unchanged" + } +} + +my @input_files = parse_options(); +help(1) if $#input_files == -1; +print STDERR "You must specify a tag\n" and help(1) if not $tag; + +foreach my $file (@input_files) +{ + process $file; +}
Property changes on: trunk/src/add-inline ___________________________________________________________________ Name: svn:executable + *
Index: trunk/src/Makefile.am =================================================================== --- trunk/src/Makefile.am (revision 474) +++ trunk/src/Makefile.am (revision 475) @@ -18,6 +18,7 @@ include ../edit.mk
dist_bin_SCRIPTS = \ +add-inline \ apatche \ checkin \ cl2patch \ Index: trunk/README =================================================================== --- trunk/README (revision 474) +++ trunk/README (revision 475) @@ -136,6 +136,11 @@ expected to deliver at the end of their first internship (at the beginning of ``Ing 2'').
+** src/add-inline +Add `inline' keywords in C++ code found in headers. This was useful for +Olena because all the code was written in headers and no inline keyword was +used, thus preventing separate compilation. + ** src/apatche Send patches to a mailing list.