blob: 40a649013007cb4074a9760670b2a12136e56af3 [file] [log] [blame]
Jim Cownie5e8470a2013-09-27 10:38:44 +00001#!/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
14use strict;
15use warnings;
16
17use FindBin;
18use lib "$FindBin::Bin/lib";
19
20use tools;
21
22our $VERSION = "0.005";
23
24my $name_rexp = qr{[A-Za-z_]+[A-Za-z0-9_]*};
25my $keyword_rexp = qr{if|else|end|omp};
26
27sub 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
36sub 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
67my ( @defines, $input, $output, $strict );
68get_options(
69 "D|define=s" => \@defines,
70 "strict!" => \$strict,
71);
72if ( @ARGV < 2 ) {
73 cmdline_error( "Not enough argument" );
74}; # if
75if ( @ARGV > 2 ) {
76 cmdline_error( "Too many argument(s)" );
77}; # if
78( $input, $output ) = @ARGV;
79
80foreach 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
109my $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.
156write_file( $output, \$bulk );
157
158exit( 0 );
159
160__END__
161
162=pod
163
164=head1 NAME
165
166B<expand-vars.pl> -- Simple text preprocessor.
167
168=head1 SYNOPSIS
169
170B<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
180Define variable.
181
182=item B<--strict>
183
184In strict mode, the script issues error on using undefined variables and executes Perl code
185with 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
197Print full help message and exit.
198
199=item B<--help>
200
201Print short help message and exit.
202
203=item B<--usage>
204
205Print very short usage message and exit.
206
207=item B<--verbose>
208
209Do print informational messages.
210
211=item B<--version>
212
213Print version and exit.
214
215=item B<--quiet>
216
217Work quiet, do not print informational messages.
218
219=back
220
221=head1 ARGUMENTS
222
223=over
224
225=item I<input>
226
227Input file name.
228
229=item I<output>
230
231Output file name.
232
233=back
234
235=head1 DESCRIPTION
236
237This script reads input file, makes substitutes and writes output file.
238
239There are two form of substitutes:
240
241=over
242
243=item Variables
244
245Variables are referenced in input file in form:
246
247 $name
248
249Name of variable should consist of alphanumeric characters (Latin letters, digits, and underscores).
250Variables are defined in command line with C<-D> or C<--define> options.
251
252=item Perl Code
253
254Perl code is specified in input file in form:
255
256 ${{ ...code... }}
257
258The code is evaluated, and is replaced with its result. Note: in strict mode, you should declare
259variable before use. See examples.
260
261=back
262
263=head1 EXAMPLES
264
Alp Toker8f2d3f02014-02-24 10:40:15 +0000265Replace occurrences of C<$year>, C<$month>, and C<$day> in C<input.txt> file with C<2007>, C<09>, C<01>
Jim Cownie5e8470a2013-09-27 10:38:44 +0000266respectively 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
273Using 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
280Using 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
287Oops, 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
292Ok, 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
297What 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 #