#!/usr/bin/perl # #//===----------------------------------------------------------------------===// #// #// The LLVM Compiler Infrastructure #// #// This file is dual licensed under the MIT and the University of Illinois Open #// Source Licenses. See LICENSE.txt for details. #// #//===----------------------------------------------------------------------===// # use strict; use warnings; use FindBin; use lib "$FindBin::Bin/lib"; use tools; our $VERSION = "0.005"; my $name_rexp = qr{[A-Za-z_]+[A-Za-z0-9_]*}; my $keyword_rexp = qr{if|else|end|omp}; sub error($$$) { my ( $input, $msg, $bulk ) = @_; my $pos = pos( $$bulk ); $$bulk =~ m{^(.*?)\G(.*?)$}m or die "Internal error"; my ( $pre, $post ) = ( $1, $2 ); my $n = scalar( @{ [ substr( $$bulk, 0, $pos ) =~ m{\n}g ] } ) + 1; runtime_error( "\"$input\" line $n: $msg:", ">>> " . $pre . "--[HERE]-->" . $post ); }; # sub error sub evaluate($$$\$) { my ( $expr, $strict, $input, $bulk ) = @_; my $value; { # Signal handler will be restored on exit from this block. # In case of "use strict; use warnings" eval issues warnings to stderr. This direct # output may confuse user, so we need to catch it and prepend with our info. local $SIG{ __WARN__ } = sub { die @_; }; $value = eval( "package __EXPAND_VARS__;\n" . ( $strict ? "use strict; use warnings;\n" : "no strict; no warnings;\n" ) . $expr ); }; if ( $@ ) { # Drop location information -- increasing eval number and constant "line 3" # is useless for the user. $@ =~ s{ at \(eval \d+\) line \d+}{}g; $@ =~ s{\s*\z}{}; error( $input, "Cannot evaluate expression \"\${{$expr}}\": $@", $bulk ); }; # if if ( $strict and not defined( $value ) ) { error( $input, "Substitution value is undefined", $bulk ); }; # if return $value; }; # sub evaluate # # Parse command line. # my ( @defines, $input, $output, $strict ); get_options( "D|define=s" => \@defines, "strict!" => \$strict, ); if ( @ARGV < 2 ) { cmdline_error( "Not enough argument" ); }; # if if ( @ARGV > 2 ) { cmdline_error( "Too many argument(s)" ); }; # if ( $input, $output ) = @ARGV; foreach my $define ( @defines ) { my ( $equal, $name, $value ); $equal = index( $define, "=" ); if ( $equal < 0 ) { $name = $define; $value = ""; } else { $name = substr( $define, 0, $equal ); $value = substr( $define, $equal + 1 ); }; # if if ( $name eq "" ) { cmdline_error( "Illegal definition: \"$define\": variable name should not be empty." ); }; # if if ( $name !~ m{\A$name_rexp\z} ) { cmdline_error( "Illegal definition: \"$define\": " . "variable name should consist of alphanumeric characters." ); }; # if eval( "\$__EXPAND_VARS__::$name = \$value;" ); if ( $@ ) { die( "Internal error: $@" ); }; # if }; # foreach $define # # Do the work. # my $bulk; # Read input file. $bulk = read_file( $input ); # Do the replacements. $bulk =~ s{(?:\$($keyword_rexp)|\$($name_rexp)|\${{(.*?)}})} { my $value; if ( defined( $1 ) ) { # Keyword. Leave it as is. $value = "\$$1"; } elsif ( defined( $2 ) ) { # Variable to expand. my $name = $2; $value = eval( "\$__EXPAND_VARS__::$name" ); if ( $@ ) { die( "Internal error" ); }; # if if ( $strict and not defined( $value ) ) { error( $input, "Variable \"\$$name\" not defined", \$bulk ); }; # if } else { # Perl code to evaluate. my $expr = $3; $value = evaluate( $expr, $strict, $input, $bulk ); }; # if $value; }ges; # Process conditionals. # Dirty patch! Nested conditionals not supported! # TODO: Implement nested constructs. $bulk =~ s{^\$if +([^\n]*) *\n(.*\n)\$else *\n(.*\n)\$end *\n} { my ( $expr, $then_part, $else_part ) = ( $1, $2, $3 ); my $value = evaluate( $expr, $strict, $input, $bulk ); if ( $value ) { $value = $then_part; } else { $value = $else_part; }; # if }gesm; # Write output. write_file( $output, \$bulk ); exit( 0 ); __END__ =pod =head1 NAME B -- Simple text preprocessor. =head1 SYNOPSIS B I