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(a)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(a)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.
--
SIGOURE Benoit aka Tsuna (SUSv3 compliant)
_____ "Jesus saves, but only Buddha
/EPITA\ Promo 2008.CSI/ACU/YAKA makes incremental backups"