Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 1 | #!/usr/bin/perl |
| 2 | |
| 3 | # |
| 4 | #//===----------------------------------------------------------------------===// |
| 5 | #// |
| 6 | #// The LLVM Compiler Infrastructure |
| 7 | #// |
| 8 | #// This file is dual licensed under the MIT and the University of Illinois Open |
| 9 | #// Source Licenses. See LICENSE.txt for details. |
| 10 | #// |
| 11 | #//===----------------------------------------------------------------------===// |
| 12 | # |
| 13 | |
| 14 | use strict; |
| 15 | use warnings; |
| 16 | |
| 17 | use FindBin; |
| 18 | use lib "$FindBin::Bin/lib"; |
| 19 | |
| 20 | use tools; |
| 21 | |
| 22 | our $VERSION = "0.005"; |
| 23 | |
| 24 | my $name_rexp = qr{[A-Za-z_]+[A-Za-z0-9_]*}; |
| 25 | my $keyword_rexp = qr{if|else|end|omp}; |
| 26 | |
| 27 | sub error($$$) { |
| 28 | my ( $input, $msg, $bulk ) = @_; |
| 29 | my $pos = pos( $$bulk ); |
| 30 | $$bulk =~ m{^(.*?)\G(.*?)$}m or die "Internal error"; |
| 31 | my ( $pre, $post ) = ( $1, $2 ); |
| 32 | my $n = scalar( @{ [ substr( $$bulk, 0, $pos ) =~ m{\n}g ] } ) + 1; |
| 33 | runtime_error( "\"$input\" line $n: $msg:", ">>> " . $pre . "--[HERE]-->" . $post ); |
| 34 | }; # sub error |
| 35 | |
| 36 | sub evaluate($$$\$) { |
| 37 | my ( $expr, $strict, $input, $bulk ) = @_; |
| 38 | my $value; |
| 39 | { # Signal handler will be restored on exit from this block. |
| 40 | # In case of "use strict; use warnings" eval issues warnings to stderr. This direct |
| 41 | # output may confuse user, so we need to catch it and prepend with our info. |
| 42 | local $SIG{ __WARN__ } = sub { die @_; }; |
| 43 | $value = |
| 44 | eval( |
| 45 | "package __EXPAND_VARS__;\n" . |
| 46 | ( $strict ? "use strict; use warnings;\n" : "no strict; no warnings;\n" ) . |
| 47 | $expr |
| 48 | ); |
| 49 | }; |
| 50 | if ( $@ ) { |
| 51 | # Drop location information -- increasing eval number and constant "line 3" |
| 52 | # is useless for the user. |
| 53 | $@ =~ s{ at \(eval \d+\) line \d+}{}g; |
| 54 | $@ =~ s{\s*\z}{}; |
| 55 | error( $input, "Cannot evaluate expression \"\${{$expr}}\": $@", $bulk ); |
| 56 | }; # if |
| 57 | if ( $strict and not defined( $value ) ) { |
| 58 | error( $input, "Substitution value is undefined", $bulk ); |
| 59 | }; # if |
| 60 | return $value; |
| 61 | }; # sub evaluate |
| 62 | |
| 63 | # |
| 64 | # Parse command line. |
| 65 | # |
| 66 | |
| 67 | my ( @defines, $input, $output, $strict ); |
| 68 | get_options( |
| 69 | "D|define=s" => \@defines, |
| 70 | "strict!" => \$strict, |
| 71 | ); |
| 72 | if ( @ARGV < 2 ) { |
| 73 | cmdline_error( "Not enough argument" ); |
| 74 | }; # if |
| 75 | if ( @ARGV > 2 ) { |
| 76 | cmdline_error( "Too many argument(s)" ); |
| 77 | }; # if |
| 78 | ( $input, $output ) = @ARGV; |
| 79 | |
| 80 | foreach my $define ( @defines ) { |
| 81 | my ( $equal, $name, $value ); |
| 82 | $equal = index( $define, "=" ); |
| 83 | if ( $equal < 0 ) { |
| 84 | $name = $define; |
| 85 | $value = ""; |
| 86 | } else { |
| 87 | $name = substr( $define, 0, $equal ); |
| 88 | $value = substr( $define, $equal + 1 ); |
| 89 | }; # if |
| 90 | if ( $name eq "" ) { |
| 91 | cmdline_error( "Illegal definition: \"$define\": variable name should not be empty." ); |
| 92 | }; # if |
| 93 | if ( $name !~ m{\A$name_rexp\z} ) { |
| 94 | cmdline_error( |
| 95 | "Illegal definition: \"$define\": " . |
| 96 | "variable name should consist of alphanumeric characters." |
| 97 | ); |
| 98 | }; # if |
| 99 | eval( "\$__EXPAND_VARS__::$name = \$value;" ); |
| 100 | if ( $@ ) { |
| 101 | die( "Internal error: $@" ); |
| 102 | }; # if |
| 103 | }; # foreach $define |
| 104 | |
| 105 | # |
| 106 | # Do the work. |
| 107 | # |
| 108 | |
| 109 | my $bulk; |
| 110 | |
| 111 | # Read input file. |
| 112 | $bulk = read_file( $input ); |
| 113 | |
| 114 | # Do the replacements. |
| 115 | $bulk =~ |
| 116 | s{(?:\$($keyword_rexp)|\$($name_rexp)|\${{(.*?)}})} |
| 117 | { |
| 118 | my $value; |
| 119 | if ( defined( $1 ) ) { |
| 120 | # Keyword. Leave it as is. |
| 121 | $value = "\$$1"; |
| 122 | } elsif ( defined( $2 ) ) { |
| 123 | # Variable to expand. |
| 124 | my $name = $2; |
| 125 | $value = eval( "\$__EXPAND_VARS__::$name" ); |
| 126 | if ( $@ ) { |
| 127 | die( "Internal error" ); |
| 128 | }; # if |
| 129 | if ( $strict and not defined( $value ) ) { |
| 130 | error( $input, "Variable \"\$$name\" not defined", \$bulk ); |
| 131 | }; # if |
| 132 | } else { |
| 133 | # Perl code to evaluate. |
| 134 | my $expr = $3; |
| 135 | $value = evaluate( $expr, $strict, $input, $bulk ); |
| 136 | }; # if |
| 137 | $value; |
| 138 | }ges; |
| 139 | |
| 140 | # Process conditionals. |
| 141 | # Dirty patch! Nested conditionals not supported! |
| 142 | # TODO: Implement nested constructs. |
| 143 | $bulk =~ |
| 144 | s{^\$if +([^\n]*) *\n(.*\n)\$else *\n(.*\n)\$end *\n} |
| 145 | { |
| 146 | my ( $expr, $then_part, $else_part ) = ( $1, $2, $3 ); |
| 147 | my $value = evaluate( $expr, $strict, $input, $bulk ); |
| 148 | if ( $value ) { |
| 149 | $value = $then_part; |
| 150 | } else { |
| 151 | $value = $else_part; |
| 152 | }; # if |
| 153 | }gesm; |
| 154 | |
| 155 | # Write output. |
| 156 | write_file( $output, \$bulk ); |
| 157 | |
| 158 | exit( 0 ); |
| 159 | |
| 160 | __END__ |
| 161 | |
| 162 | =pod |
| 163 | |
| 164 | =head1 NAME |
| 165 | |
| 166 | B<expand-vars.pl> -- Simple text preprocessor. |
| 167 | |
| 168 | =head1 SYNOPSIS |
| 169 | |
| 170 | B<expand-vars.pl> I<OPTION>... I<input> I<output> |
| 171 | |
| 172 | =head1 OPTIONS |
| 173 | |
| 174 | =over |
| 175 | |
| 176 | =item B<-D> I<name>[B<=>I<value>] |
| 177 | |
| 178 | =item B<--define=>I<name>[B<=>I<value>] |
| 179 | |
| 180 | Define variable. |
| 181 | |
| 182 | =item B<--strict> |
| 183 | |
| 184 | In strict mode, the script issues error on using undefined variables and executes Perl code |
| 185 | with C<use strict; use warnings;> pragmas. |
| 186 | |
| 187 | =back |
| 188 | |
| 189 | =head2 Standard Options |
| 190 | |
| 191 | =over |
| 192 | |
| 193 | =item B<--doc> |
| 194 | |
| 195 | =item B<--manual> |
| 196 | |
| 197 | Print full help message and exit. |
| 198 | |
| 199 | =item B<--help> |
| 200 | |
| 201 | Print short help message and exit. |
| 202 | |
| 203 | =item B<--usage> |
| 204 | |
| 205 | Print very short usage message and exit. |
| 206 | |
| 207 | =item B<--verbose> |
| 208 | |
| 209 | Do print informational messages. |
| 210 | |
| 211 | =item B<--version> |
| 212 | |
| 213 | Print version and exit. |
| 214 | |
| 215 | =item B<--quiet> |
| 216 | |
| 217 | Work quiet, do not print informational messages. |
| 218 | |
| 219 | =back |
| 220 | |
| 221 | =head1 ARGUMENTS |
| 222 | |
| 223 | =over |
| 224 | |
| 225 | =item I<input> |
| 226 | |
| 227 | Input file name. |
| 228 | |
| 229 | =item I<output> |
| 230 | |
| 231 | Output file name. |
| 232 | |
| 233 | =back |
| 234 | |
| 235 | =head1 DESCRIPTION |
| 236 | |
| 237 | This script reads input file, makes substitutes and writes output file. |
| 238 | |
| 239 | There are two form of substitutes: |
| 240 | |
| 241 | =over |
| 242 | |
| 243 | =item Variables |
| 244 | |
| 245 | Variables are referenced in input file in form: |
| 246 | |
| 247 | $name |
| 248 | |
| 249 | Name of variable should consist of alphanumeric characters (Latin letters, digits, and underscores). |
| 250 | Variables are defined in command line with C<-D> or C<--define> options. |
| 251 | |
| 252 | =item Perl Code |
| 253 | |
| 254 | Perl code is specified in input file in form: |
| 255 | |
| 256 | ${{ ...code... }} |
| 257 | |
| 258 | The code is evaluated, and is replaced with its result. Note: in strict mode, you should declare |
| 259 | variable before use. See examples. |
| 260 | |
| 261 | =back |
| 262 | |
| 263 | =head1 EXAMPLES |
| 264 | |
Alp Toker | 8f2d3f0 | 2014-02-24 10:40:15 +0000 | [diff] [blame] | 265 | Replace occurrences of C<$year>, C<$month>, and C<$day> in C<input.txt> file with C<2007>, C<09>, C<01> |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 266 | respectively and write result to C<output.txt> file: |
| 267 | |
| 268 | $ cat input.var |
| 269 | Today is $year-$month-$day. |
| 270 | $ expand-vars.pl -D year=2007 -D month=09 -D day=01 input.var output.txt && cat output.txt |
| 271 | Today is 2007-09-01. |
| 272 | |
| 273 | Using Perl code: |
| 274 | |
| 275 | $ cat input.var |
| 276 | ${{ localtime(); }} |
| 277 | $ expand-vars.pl -D year=2007 -D month=09 -D day=01 input.var output.txt && cat output.txt |
| 278 | Now Tue May 5 20:54:13 2009 |
| 279 | |
| 280 | Using strict mode for catching bugs: |
| 281 | |
| 282 | $ cat input.var |
| 283 | ${{ "year : " . substr( $date, 0, 4 ); }} |
| 284 | $ expand-vars.pl input.var output.txt && cat output.txt |
| 285 | year : |
| 286 | |
| 287 | Oops, why it does not print year? Let us use strict mode: |
| 288 | |
| 289 | $ expand-vars.pl --strict input.var output.txt && cat output.txt |
| 290 | expand-vars.pl: (x) "test.var": Cannot evaluate expression "${{ "year : " . substr( $date, 0, 4 ); }}": Global symbol "$date" requires explicit package name |
| 291 | |
| 292 | Ok, variable is not defined. Let us define it: |
| 293 | |
| 294 | $ expand-vars.pl --strict -D date=20090501 input.var output.txt && cat output.txt |
| 295 | expand-vars.pl: (x) "test.var": Cannot evaluate expression "${{ "year : " . substr( $date, 0, 4 ); }}": Variable "$date" is not imported |
| 296 | |
| 297 | What is wrong? Variable should be declared: |
| 298 | |
| 299 | $ cat input.var |
| 300 | ${{ our $date; "year : " . substr( $date, 0, 4 ); }} |
| 301 | $ expand-vars.pl --strict -D date=20090501 input.var output.txt && cat output.txt |
| 302 | year : 2009 |
| 303 | |
| 304 | =cut |
| 305 | |
| 306 | # end of file # |