From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,38159b1b5557a2e7 X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 2004-01-25 20:00:02 PST Path: archiver1.google.com!postnews1.google.com!not-for-mail From: prichtmyer@yahoo.com (Peter Richtmyer) Newsgroups: comp.lang.ada Subject: Re: Standard Ada Preprocessor Date: 25 Jan 2004 20:00:00 -0800 Organization: http://groups.google.com Message-ID: <1b585154.0401252000.cf008c5@posting.google.com> References: <400A9B48.3060100@noplace.com> <400BD4B5.6000307@noplace.com> <400BDB7C.40100@noplace.com> <400D2150.6000705@noplace.com> NNTP-Posting-Host: 68.110.200.186 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: posting.google.com 1075089601 23684 127.0.0.1 (26 Jan 2004 04:00:01 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Mon, 26 Jan 2004 04:00:01 +0000 (UTC) Xref: archiver1.google.com comp.lang.ada:4797 Date: 2004-01-25T20:00:00-08:00 List-Id: I have not read the entire thread. Bottom lines for me: 1) too bad Ada does not have a preprocessor 2) I needed one, so I created one It has served me well for 4 years on Win2000, Solaris, HP/UX, and LynxOS. Not elegant but very useful to me, to take code home at night, develop/test on Win2000, then back to work with the same source code (preparsed) to continue to develop/test on Solaris (or other). I have included some files: PreProcess.pm -- the preprocessor PreCheck.pm -- checks for conditionals and some sample DOS batch files Use if you like, ignore otherwise :-) I hope it will help someone out there. Peter ======================== PreProcess.pm ========================== #!/usr/bin/perl -s package Devel::PreProcess; #IO: use IO::File; #use vars qw( $Conditionals $StripPreserve $StripDelete ); #use vars qw( $CMNT $CNDT $GenAda $fh $fhout $to_file $line_number ); $RETURN_CODE = 0; # code returned to system or caller # 0 - OK # -1 - Command Line option error. # -2 - Could not open the input file or none specified. # -3 - Could not open the output file or none specified. goto PAST_HELP; #### We put 'HELP' up here so you can see it easily. :-) PRINT_HELP: print <<"END_HELP"; Devel::PreProcess v 1.7 01/25/04 Generate conditional compile code for source code >From a command line, sh> perl -s Devel/PreProcess.pm -Flags sourcefile > targetfile or sh> perl -s Devel/PreProcess.pm -Flags sourcefile [targetfile] Or in a Perl script, use Devel::PreProcess qw( Flags ); Devel::PreProcess::parse_file( $sourcefile [, $targetfile] ); DESCRIPTION This package processes source files and outputs a modified version according to several user-setable option flags, as detailed below. Each of the flag names listed below can be used as above, with a hyphen on the command line, or as one of the arguments in an import statement. Each of these flags are mapped to the scalar package variable of the same name. Option -Conditionals (True is the default) (Use Noconditionals to turn it off) If true, parse_file will utilize a simple conditional inclusion scheme, as follows. --#__COND__ if expr1 ... --#__COND__ elsif expr2 ... --#__COND__ else ... --#__COND__ endif The "elsif" line is optional, and there may be more than one such line. The "else" line is optional. The provided Perl expression (expr1) is evaluated, and unless it is true, everything up to the next conditional declaration is made into special comment lines ("--#"). If "false" then sucessive elsif conditionals, if preset, are evaluated as in any "if ... elsif ... else ... endif" logic. In addition, a single line may be conditional'd using the following format: --#{expr3} source code first line here; some more source code on second line; --#{expr4} When these expressions (expr3 and expr4) are evaluated, when True then the line will be output in the format of the second line above so that the Ada code is active. When an expression is False, then the output will be in the format of the second above. For example, lines below were preprocessed with "-demo" on command line: -- TESTING: this is demo line --#{$demo} --#{!$demo} -- TESTING: not a demo line WARNING: This does not work correctly in some environments unless there are spaces after the "}" and/or before the "--#" when at the end of a line. The conditional functionality can be combined with Perl's -s switch, which allows you to set flags on the command line, such as: perl -s Devel/PreProcess.pm -Switch filter.test You can use any name for your switch, and the matching scalar variable will be set true; the following code will only be used if you supply the argument as shown below. --#__COND__ if $Switch --# put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif If the Switch variable (entered on the command line as in the example above) is true then the output file will contain: --#__COND__ if $Switch put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif Thus, the two Ada lines of code will be compiled. If the Switch variable is false then the output file will contain: --#__COND__ if $Switch --# put_line ("you hit the switch!"); --# put_line ("so these two lines will be printed"); --#__COND__ endif Since the "if" and "elsif" conditions are Perl conditionals, they can be compound conditionals. Use "&&" for AND, "||" for OR, etc. And use the "!" for NOT. For example: --#__COND__ if ($green && ! ($apple || $pear)) In addition, the "if ... endif" blocks can be nested. In reality, it is legal to have white-space between the "--#" and the "__COND__" portions of a conditional statement. It should be noted that one of the strengths of this preprocessor is that the output from one "run" can be used as the input to the next run. For example, if code is being developed on two different computing environments (e.g. home and work), then the code can be generated using a "-home" switch. The code would be developed and tested some at home, with changes made. Then the code would be run through with the "-work" switch set, and development and test would continue on the same source code at work. Option -StripPreserve This option will cause all lines beginning with the Conditional Comment prefix ("--#" or its replacement using option "CMNT" described below) on the output side of the logic to be replaced with a blank line in the output stream. Option -StripDelete This option will cause all lines beginning with the Conditional Comment prefix ("--#" or its replacement) on the output side of the logic to be deleted, that is, not written to the output stream. Option -CMNT="comment-string" This option will replace the Conditional Comment string "--#" with the "comment-string" provided. Thus -CMNT="--$$$" would specify an alternative for conditional comment string for Ada, while -CMNT="#*#*" could be used to specify a conditional comment string when preparsing Perl source code. Option -CNDT="conditional-string" This option will replace the Conditional string "__COND__" with the "conditional-string" provided. This is the string that comes after the CMNT string to specify a line with "if", "elsif", "else" and "endif" statements. Option -LEFT="left-side" This option will replace the "{" as the string on the left side of a single-line conditional with "left-side" Option -RIGHT="right-side" This option will replace the "}" as the string on the right side of a single-line conditional with "right-side" So, if entered " -LEFT="{{" -RIGHT="}}" then the single-line conditionals would be similar to: --#{{expr5}} Care must be taken, certain special charaters need to be "escaped". For example, a left paren by itself will not work (you will get a program error). So: -LEFT="(" will not work, use -LEFT="\(" BUGS AND CAVEATS Option Flags -CMNT and -CNDT msy not work when calling from another Perl script. (Workaround: just make a separate copy of the pgm that has the desired defaults.) END_HELP exit ($RETURN_CODE); ################3############## Change History ################################# # ver date description # --- -------- --------------------------------------------------------------- # 1.7 01/25/04 Editorial changes only # 1.6 07/10/03 Minor pattern-matching change # 1.5 03/26/01 If the output is a directory, then use the same filename # as the input, for writing to the output directory. # 1.4b 11/07/00 Some clean-up, comments, deleted code, ... # 1.4a 9/26/00 Corrected "help" # 1.4 09/25/00 Had to make changes to work with perl5 on HP. # 1.3a 9/25/00 Documentation update only. No CODE changes. # 1.3 03/06/00 Adding the single-line conditional. --#{$test} # 1.2 02/28/00 Clean up. # # 1.1 02/27/00 Commenting out the "IO::File" logic with #IO: prefix and # replacing it with old-style I/O so I can run for now on systems # that do not have the IO package. # # 1.0 02/26/00 Original PAST_HELP: # # Change the variables below to change the "syntax" of the conditionals: # $CMNT = "--#"; $CNDT = "__COND__"; $LEFT = "{"; $RIGHT = "}"; # $Conditionals = 1; # @psuedo_INC - directories to search for modules #use vars qw( @psuedo_INC ); # really constants below - is there better way of making constants in Perl 5? #use vars qw( $EOF $ELSIF $ELSE $ENDIF ); $EOF = 1; $ELSIF = 2; $ELSE = 3; $ENDIF = 4; # %Importers - maps file inclusion keywords to their import methods #use vars qw( %Importers ); #%Importers = ( # 'require' => '', # 'use' => 'import', # 'no' => 'unimport', #); # Devel::PreProcessor->import( 'StripPreserve', 'Conditionals', ... ); sub import { my $package = shift; foreach ( @_ ) { if ( m/Conditionals/i ) { $Conditionals = 1; } elsif ( m/Noconditionals/i ) { ### PMR not tested yet $Conditionals = 0; ### PMR not tested yet } elsif ( m/h/i ) { goto PRINT_HELP; } elsif ( m/help/i ) { goto PRINT_HELP; } elsif ( m/StripPreserve/i ) { $StripPreserve = 1; } elsif ( m/StripDelete/i ) { $StripDelete = 1; } elsif ( m/LibPath:(.*)/i ) { @INC = split(/\:/, $1); } elsif ( m/CMNT=(.*)/) { ### PMR not tested yet $CMNT = $1; ### PMR not tested yet } elsif ( m/CNDT=(.*)/) { ### PMR not tested yet $CNDT = $1; ### PMR not tested yet } elsif ( m/RIGHT=(.*)/) { $RIGHT = $1; } elsif ( m/LEFT=(.*)/) { $LEFT = $1; } else { die "unkown import"; } } } # If we're being run from the command line, look for certain options # on the command line after program name. unless ( caller ) { goto PRINT_HELP if ( $main::help || $main::h ) ; goto PRINT_HELP if ( $main::Help || $main::H ) ; $Conditionals = 0 if ( $main::Noconditionals ); $Conditionals ||= $main::Conditionals; $StripPreserve ||= $main::StripPreserve; $StripDelete ||= $main::StripDelete; $CMNT = $main::CMNT if ($main::CMNT); $CNDT = $main::CNDT if ($main::CNDT); $LEFT = $main::LEFT if ($main::LEFT); $RIGHT = $main::RIGHT if ($main::RIGHT); my $in_source = shift @ARGV; # input filename my $out_source = shift @ARGV; # output filename parse_file ( $in_source, $out_source ); } # ------------------------------ parse_file ------------------------------------ # # Calling: parse_file ( $infilename [, $outfilename] ); # # This routine is called by the mainline above. But it can also be called # by an external program. # sub parse_file { # # before doing much, let's make sure that any overrides to conditional # matching variables are legal. # ## NEED to get this to work, and one for the {$test} if ( eval ("$line =~ /$CMNT\s*$CNDT/") ) { print "ERROR: You entered an illegal string for CMNT or CNDT \n"; print " CMNT='$CMNT', CNDT='$CNDT' \n"; print " Use a backslash '\\' before certain special characters. \n"; die "Try Again"; } my $infilename = shift; # the file to preparse #IO: $fh = IO::File->new($infilename); # global inputfile handle open ( INPUT, $infilename ) || die "Unable to open input file $infilename"; my $outfilename = shift; # the file to write to if ($outfilename) { # if output file specified # ----------------------------------------------------------- # if the output filename is a directory, just use it and # the input filename for the complete output name. # ----------------------------------------------------------- if (-d $outfilename) { @inparts = split(/[\\\/]/, $infilename); # split into parts separated by "\" or "/" $fname = $inparts[$#inparts]; # the last part is the input file name $outfilename .= "/$fname"; } #IO: $fhout = IO::File->new("> $outfilename"); # open output file open ( OUTPUT, "> $outfilename" ) || die "Unable to open output file $outfilename" ; $to_file = 1; # "writing to file" } else { # else $to_file = 0; # "writing to stdout" } my $top_eof = parse_block ( 1, 1 ); # parse entire file die "Program terminating before End-of-File - too many endif's ? " if ($top_eof != $EOF); } # ------------------------------ parse_block ------------------------------------- # # $rtn = parse_block ( $level, $GenAda ); # # Parses a "block" of code. If "$level" = 1, then this "block" is the entire file. # Since we always want to generate Ada for the file-level, $GenAda = 1 for level = 1. # While parsing lines in a block, if we find a "conditional if", then that is # the start of an embedded block, and this routine is called recursively to parse # that block. # # $rtn - these are the values that can be passed back from parse_block, and they # tell you why we returned from the routine. If not "EOF", then $_ has the # line of source code with the Conditional Commented statement, and it has # not been printed yet. # $EOF = 1; # $ELSIF = 2; # $ELSE = 3; # $ENDIF = 4; # sub parse_block { my $level = shift; # What level of recursion (1 = outermost) my $GenAda = shift; # Whether it is OK to generate Ada # out of cond-commented code LINE: while( 1 ) { # # Get a new line of code. If we hit eof, then return EOF. # #IO: return ($EOF) if (! defined ($_ = $fh->getline) ); return ($EOF) if (! ($_ = ) ); $line_number ++; # # if this is a COND "endif", "elsif", or "else" then # return to previous block with the code for this line # return ($ENDIF) if ( $_ =~ /^\s*$CMNT\s*$CNDT\s*endif/i ); return ($ELSIF) if ( $_ =~ /^\s*$CMNT\s*$CNDT\s*elsif\s+/i ); return ($ELSE) if ( $_ =~ /^\s*$CMNT\s*$CNDT\s*else\s+/i ); # # If this line is a special conditional "if" # evaluate the conditional that is on the 'if' # write it if writeing cond comments # call this routine recursively with level nbr and conditional results # if ( $Conditionals && /^\s*$CMNT\s*$CNDT\s?(.*)$/i ) { my $conditional = $1; if ( $conditional =~ s/^if //i ) { write_if ("$_"); # write cond "if" line "as-is" (unless stripping) my $This_GenAda = eval "package main;\n$conditional"; my $We_Had_GenAda = 0; # # Why $We_Had_GenAda ? # If this "if block" turns out ot be a complex block such as "if-elsif-else" # Then we need to keep track of whether or not we have had one "true" # sub-block within it. After one true sub-block, all others must be false. # Once we have had that "true" sub-block, $We_Had_GenAda will = true. # SUB_BLOCK: while ( 1 ) { my $eob = parse_block ( ($level + 1), ($This_GenAda && $GenAda && (not $We_Had_GenAda )) ); $We_Had_GenAda = ($This_GenAda && $GenAda) || $We_Had_GenAda; die "End-of-File reached while in a Conditional Compile block" if ($eof); write_if ("$_"); # write line that ended block / sub-block next LINE if ($eob == $ENDIF); # done with entire block if ($eob == $ELSE) { $This_GenAda = ! $We_Had_GenAda; # set up this block GenAda #$This_GenAda = not $We_Had_GenAda; # set up this block GenAda next SUB_BLOCK; # parse the "else" subblock } die "Program error - bad EOB" if ($eob != $ELSIF); # Parse the "condition" part out of line and evaluate it true / false /^\s*$CMNT\s*$CNDT\s?elsif(.*)$/i; $conditional = "$1"; $This_GenAda = eval "package main;\n$conditional"; # will loop back to SUB_BLOCK and parse this "elsif" sub-block. } } else { print "$_"; die "conditional above is out of place"; } } # look for a single line conditional. local ($SCOND) = ""; local ($SLINE) = ""; if ( /^(\s*$CMNT\s*$LEFT.*$RIGHT)(.*)$/ ) { #print "TEMP found $1 in --#\${LEFT}xxxx\$RIGHT} line and $2 after it\n"; $SCOND = $1; $SLINE = $2; } if ( /^(.*)($CMNT\s*$LEFT.*$RIGHT\s*)$/ ) { #print "TEMP found '$1' before '$2' \n"; $SCOND = $2; chomp($SCOND); $SLINE = $1; } if ( $SCOND ) { $SCOND =~ /$CMNT\s*$LEFT(.*)$RIGHT/; if ( eval "package main;\n$1" ) { &write_if ("$SLINE $SCOND\n"); } else { &write_if ("$SCOND $SLINE\n"); } next LINE; } # # If we are in the outer level (not within a COND block), then we do not even # have to look at the line of code. Just write it (if..) # if ($level == 1) { # # We could make a check here for a "--$COMM" line. That would be a straggler, # outside of a conditional, so we could alert the officials about it. # write_if ("$_"); # local routine to write if OK to write next LINE; } # # $GenAda can only be true if we are processing conditionals and we found # a conditional that evaluated True. # ##print " \$GenAda = $GenAda \n"; if ($GenAda) { # # This line must NOT be a COND comment. If it is now, # then remove the COND part. # if ( /^\s*$CMNT(.*)$/i ) { write_if ("$1\n"); } else { write_if ("$_"); } next LINE; } # # If we are processing conditionals and this line should be commented out, # then make sure that it is a conditional comment before calling write routine. # #if ($Conditionals && not $GenAda) { if ($Conditionals && ! $GenAda) { if ( /^\s*$CMNT/ ) { write_if ("$_"); } else { write_if ("$CMNT$_"); } next LINE; } die "We found a condition that this humble programmer did not anticipate."; } # end loop } # end sub parse_block # --------------------------------------------------------------------------------------- #sub write_if ($line); # # write the line unless the "StripPreserve" or "StripDelete" flag is set # and this is a conditional comment. # sub write_if { $_ = shift; # if ( /^\s*$CMNT/ ) { # if a cond commment return if ($StripDelete); # delete the line if we are strip-deleting. if ($StripPreserve) { # if strip-replacing with blank line write_line ("\n"); # substitute a blank line return; # } # } # If this is NOT a comment, but has the "one-line" conditional at the right end # and we are stripping comments, Then we want to keep the line except for the # conditional at the right end. # if ( /^(.*) ($CMNT\s*$LEFT.*$RIGHT\s*)$/ ) { if ( $StripDelete || $StripPreserve ) { write_line ("$1\n"); return; } } write_line ("$_"); # write the line as-is } # --------------------------------------------------------------------------------------- # # Write it to stdout or the file (if specified). # sub write_line { my $line = shift; if ( $to_file ) { #IO: $fhout->print ($line); print OUTPUT "$line"; } else { print "$line"; } } #----------- 1; __END__ =head1 NAME Devel::PreProcess - Source code preprocessing. =head1 SYNOPSIS >From a command line, sh> perl Devel/PreProcess.pm -Flags sourcefile > targetfile or sh> perl Devel/PreProcess.pm -Flags sourcefile [targetfile] Or in a Perl script, use Devel::PreProcess qw( Flags ); Devel::PreProcess::parse_file( $sourcefile [, $targetfile] ); =head1 DESCRIPTION This package processes source files and outputs a modified version according to several user-setable option flags, as detailed below. Each of the flag names listed below can be used as above, with a hyphen on the command line, or as one of the arguments in an import statement. Each of these flags are mapped to the scalar package variable of the same name. =over 4 =item Conditionals (True is the default) (Use Noconditionals to turn it off) If true, parse_file will utilize a simple conditional inclusion scheme, as follows. --#__COND__ if expr1 ... --#__COND__ elsif expr2 ... --#__COND__ else ... --#__COND__ endif The "elsif" line is optional, and there may be more than one such line. The "else" line is optional. The provided Perl expression (expr1) is evaluated, and unless it is true, everything up to the next conditional declaration is made into comment lines. If "false" then sucessive elsif conditionals, if preset, are evaluated as in any "if ... elsif ... else ... endif" logic. The conditional functionality can be combined with Perl's C<-s> switch, which allows you to set flags on the command line, such as: perl -s Devel/PreProcess.pm -Conditionals -Switch filter.test You can use any name for your switch, and the matching scalar variable will be set true; the following code will only be used if you supply the argument as shown below. --#__COND__ if $Switch --# put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif If the Switch variable is true then the output file will contain: --#__COND__ if $Switch put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif Thus, the two Ada lines of code will be compiled. If the Switch variable is false then the output file will contain: --#__COND__ if $Switch --# put_line ("you hit the switch!"); --# put_line ("so these two lines will be printed"); --#__COND__ endif Since the "if" and "elsif" conditions are Perl conditionals, they can be a compound conditional. Use "&&" for AND, "||" for OR, etc. For example: --#__COND__ if ($green && ($apple || $pear)) In addition, the "if ... endif" blocks can be nested. In reality, it is legal to have white-space between the "--#" and the "__COND__" portions of a conditional statement. =item StripPreserve This option will cause all lines beginning with the Conditional Comment prefix ("--#" or its replacement) on the output side of the logic to be replaced with a blank line in the output stream. =item StripDelete This option will cause all lines beginning with the Conditional Comment prefix ("--#" or its replacement) on the output side of the logic to be deleted, that is, not written to the output stream. =item CMNT="comment-string" This option will replace the Conditional Comment string "--#" with the "comment-string" provided. Thus -CMNT="--$$$" would specify an alternative for conditional comment string for Ada, while -CMNT="#*#*" could be used to specify a conditional comment string when preparsing Perl source code. =item CNDT="conditional-string" This option will replace the Conditional string "__COND__" with the "conditional-string" provided. This is the string that comes after the CMNT string to specify a line with "if", "elsif", "else" and "endif" statements. =back =head1 EXAMPLES =head1 BUGS AND CAVEATS Option Flags -CMNT and -CNDT may not work when calling from another Perl script. (Workaround: just make a separate copy of the pgm that has the desired defaults.) =head1 PREREQUISITES This package should run on standard Perl 5 installations, but may not run on earliest Perl 5 releases or incomplete installations. =head1 STATUS AND SUPPORT This release of Devel::PreProcess is intended primarily for limited private use. Not all options (especially "caller" options) have been tested. =head1 AUTHORS AND COPYRIGHT This program was adapted from a Devel::PreProcessor.pm from Evolution Online Systems, Inc. Ewww.evolution.comE, which had Copyright restrictions on it. It is felt that the changes to it are significant enought that the Copyright is not applicable. Nevertheless, credit is given to them for the basic program structure. Also, an email from them said it is OK to use it in this way. The modifications were made by Peter Richtmyer, RISYS, Portsmouth, RI. Others may use and modify it, just don't sell it or package it for sale. ============================= PreCheck.pm =============================== #!/usr/local/bin/perl -s package Devel::PreCheck; #IO: use IO::File; #use vars qw( $Conditionals $StripPreserve $StripDelete ); #use vars qw( $CMNT $CNDT $GenAda $fh $fhout $to_file $line_number ); $RETURN_CODE = 0; # code returned to system or caller # 0 - OK # -1 - Command Line option error. # -2 - Could not open the input file or none specified. # -3 - Could not open the output file or none specified. goto PAST_HELP; #### We put 'HELP' up here so you can see it easily. :-) PRINT_HELP: print <<"END_HELP"; Devel::PreCheck v 2.1 Check for conditional compile code in source code >From a command line, sh> perl -s Devel/PreCheck.pm -Flags sourcefile Or in a Perl script, use Devel::PreProcess qw( Flags ); Devel::PreProcess::parse_file( $sourcefile ); DESCRIPTION This package processes source files and outputs a 0 - no conditional statements found 1 - found Each of the flag names listed below can be used as above, with a hyphen on the command line, or as one of the arguments in an import statement. Each of these flags are mapped to the scalar package variable of the same name. Option -Conditionals (True is the default) (Use Noconditionals to turn it off) If true, parse_file will utilize a simple conditional inclusion scheme, as follows. --#__COND__ if expr1 ... --#__COND__ elsif expr2 ... --#__COND__ else ... --#__COND__ endif The "elsif" line is optional, and there may be more than one such line. The "else" line is optional. The provided Perl expression (expr1) is evaluated, and unless it is true, everything up to the next conditional declaration is made into special comment lines ("--#"). If "false" then sucessive elsif conditionals, if preset, are evaluated as in any "if ... elsif ... else ... endif" logic. In addition, a single line may be conditional'd using the following format: --#{expr3} source code first line here; some more source code on second line; --#{expr4} When these expressions (expr3 and expr4) are evaluated, when True then the line will be output in the format of the second line above so that the Ada code is active. When an expression is False, then the output will be in the format of the second above. For example, lines below were preprocessed with "-demo" on command line: -- TESTING: this is demo line --#{$demo} --#{!$demo} -- TESTING: not a demo line WARNING: This does not work correctly in some environments unless there are spaces after the "}" and/or before the "--#" when at the end of a line. The conditional functionality can be combined with Perl's -s switch, which allows you to set flags on the command line, such as: perl -s Devel/PreProcess.pm -Switch filter.test You can use any name for your switch, and the matching scalar variable will be set true; the following code will only be used if you supply the argument as shown below. --#__COND__ if $Switch --# put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif If the Switch variable (entered on the command line as in the example above) is true then the output file will contain: --#__COND__ if $Switch put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif Thus, the two Ada lines of code will be compiled. If the Switch variable is false then the output file will contain: --#__COND__ if $Switch --# put_line ("you hit the switch!"); --# put_line ("so these two lines will be printed"); --#__COND__ endif Since the "if" and "elsif" conditions are Perl conditionals, they can be compound conditionals. Use "&&" for AND, "||" for OR, etc. And use the "!" for NOT. For example: --#__COND__ if ($green && ! ($apple || $pear)) In addition, the "if ... endif" blocks can be nested. In reality, it is legal to have white-space between the "--#" and the "__COND__" portions of a conditional statement. It should be noted that one of the strengths of this preprocessor is that the output from one "run" can be used as the input to the next run. For example, if code is being developed on two different computing environments (e.g. home and work), then the code can be generated using a "-home" switch. The code would be developed and tested some at home, with changes made. Then the code would be run through with the "-work" switch set, and development and test would continue on the same source code at work. Option -CMNT="comment-string" This option will replace the Conditional Comment string "--#" with the "comment-string" provided. Thus -CMNT="--$$$" would specify an alternative for conditional comment string for Ada, while -CMNT="#*#*" could be used to specify a conditional comment string when preparsing Perl source code. Option -CNDT="conditional-string" This option will replace the Conditional string "__COND__" with the "conditional-string" provided. This is the string that comes after the CMNT string to specify a line with "if", "elsif", "else" and "endif" statements. Option -LEFT="left-side" This option will replace the "{" as the string on the left side of a single-line conditional with "left-side" Option -RIGHT="right-side" This option will replace the "}" as the string on the right side of a single-line conditional with "right-side" So, if entered " -LEFT="{{" -RIGHT="}}" then the single-line conditionals would be similar to: --#{{expr5}} Care must be taken, certain special charaters need to be "escaped". For example, a left paren by itself will not work (you will get a program error). So: -LEFT="(" will not work, use -LEFT="\(" BUGS AND CAVEATS Option Flags -CMNT and -CNDT msy not work when calling from another Perl script. (Workaround: just make a separate copy of the pgm that has the desired defaults.) END_HELP exit ($RETURN_CODE); ################3############## Change History ################################# # ver date description # --- -------- ----------------------------------------------------------------- # 1.0 03/26/00 Original, copied from PreProcess.pm # 2.0 06/08/03 In addition to looking for a string like "--#__COND__" # at the beginning of a line, must also look for a # string like "--#{...}" at the beginning or end of a line. # 2.1 07/10/03 Minor pattern-matching change PAST_HELP: # # Change the variables below to change the "syntax" of the conditionals: # $CMNT = "--#"; $CNDT = "__COND__"; $LEFT = "{"; $RIGHT = "}"; # $Conditionals = 1; # @psuedo_INC - directories to search for modules #use vars qw( @psuedo_INC ); # %Importers - maps file inclusion keywords to their import methods #use vars qw( %Importers ); #%Importers = ( # 'require' => '', # 'use' => 'import', # 'no' => 'unimport', #); # Devel::PreProcessor->import( 'StripPreserve', 'Conditionals', ... ); sub import { my $package = shift; foreach ( @_ ) { if ( m/Conditionals/i ) { $Conditionals = 1; } elsif ( m/Noconditionals/i ) { ### PMR not tested yet $Conditionals = 0; ### PMR not tested yet } elsif ( m/h/i ) { goto PRINT_HELP; } elsif ( m/help/i ) { goto PRINT_HELP; } elsif ( m/LibPath:(.*)/i ) { @INC = split(/\:/, $1); } elsif ( m/CMNT=(.*)/) { ### PMR not tested yet $CMNT = $1; ### PMR not tested yet } elsif ( m/CNDT=(.*)/) { ### PMR not tested yet $CNDT = $1; ### PMR not tested yet } elsif ( m/RIGHT=(.*)/) { $RIGHT = $1; } elsif ( m/LEFT=(.*)/) { $LEFT = $1; } else { die "unkown import"; } } } # If we're being run from the command line, look for certain options # on the command line after program name. unless ( caller ) { goto PRINT_HELP if ( $main::help || $main::h ) ; goto PRINT_HELP if ( $main::Help || $main::H ) ; $Conditionals = 0 if ( $main::Noconditionals ); $Conditionals ||= $main::Conditionals; $CMNT = $main::CMNT if ($main::CMNT); $CNDT = $main::CNDT if ($main::CNDT); $LEFT = $main::LEFT if ($main::LEFT); $RIGHT = $main::RIGHT if ($main::RIGHT); my $in_source = shift @ARGV; # input filename parse_file ( $in_source ); } # ------------------------------ parse_file ------------------------------------ # # Calling: parse_file ( $infilename ); # # This routine is called by the mainline above. But it can also be called # by an external program. # sub parse_file { # # before doing much, let's make sure that any overrides to conditional # matching variables are legal. # ## NEED to get this to work, and one for the {$test} if ( eval ("$line =~ /$CMNT\s*$CNDT/") ) { print "ERROR: You entered an illegal string for CMNT or CNDT \n"; print " CMNT='$CMNT', CNDT='$CNDT' \n"; print " Use a backslash '\\' before certain special characters. \n"; die "Try Again"; } my $infilename = shift; # the file to preparse #IO: $fh = IO::File->new($infilename); # global inputfile handle #print ("open ( INPUT, $infilename ) \n"); open ( INPUT, $infilename ) || die "Unable to open input file $infilename"; while () { # print "looking for $CMNT$CNDT in: $_\n"; if ( /^\s*$CMNT\s*$CNDT/i ) { print "Found conditional in: file $infilename \n"; close (INPUT); exit 1; # found conditional } # print "looking for $CMNT$LEFT...$RIGHT at beginning of: $_\n"; if ( /^(\s*$CMNT\s*$LEFT.*$RIGHT)(.*)$/ ) { print "Found conditional in: file $infilename \n"; close (INPUT); exit 1; # found conditional } print "looking for $CMNT$LEFT...$RIGHT at end of: $_\n"; if ( /^(.*)($CMNT\s*$LEFT.*$RIGHT\s*)$/ ) { print "Found conditional in: file $infilename \n"; close (INPUT); exit 1; # found conditional } } print "DID NOT FIND CONDITIONAL in FILE: $infilename \n"; close (INPUT); exit 0; # no conditionals found } #----------- 1; __END__ =head1 NAME Devel::PreProcess - Source code preprocessing. =head1 SYNOPSIS >From a command line, sh> perl Devel/PreProcess.pm -Flags sourcefile > targetfile or sh> perl Devel/PreProcess.pm -Flags sourcefile [targetfile] Or in a Perl script, use Devel::PreProcess qw( Flags ); Devel::PreProcess::parse_file( $sourcefile [, $targetfile] ); =head1 DESCRIPTION This package processes source files and outputs a modified version according to several user-setable option flags, as detailed below. Each of the flag names listed below can be used as above, with a hyphen on the command line, or as one of the arguments in an import statement. Each of these flags are mapped to the scalar package variable of the same name. =over 4 =item Conditionals (True is the default) (Use Noconditionals to turn it off) If true, parse_file will utilize a simple conditional inclusion scheme, as follows. --#__COND__ if expr1 ... --#__COND__ elsif expr2 ... --#__COND__ else ... --#__COND__ endif The "elsif" line is optional, and there may be more than one such line. The "else" line is optional. The provided Perl expression (expr1) is evaluated, and unless it is true, everything up to the next conditional declaration is made into comment lines. If "false" then sucessive elsif conditionals, if preset, are evaluated as in any "if ... elsif ... else ... endif" logic. The conditional functionality can be combined with Perl's C<-s> switch, which allows you to set flags on the command line, such as: perl -s Devel/PreProcess.pm -Conditionals -Switch filter.test You can use any name for your switch, and the matching scalar variable will be set true; the following code will only be used if you supply the argument as shown below. --#__COND__ if $Switch --# put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif If the Switch variable is true then the output file will contain: --#__COND__ if $Switch put_line ("you hit the switch!"); put_line ("so these two lines will be printed"); --#__COND__ endif Thus, the two Ada lines of code will be compiled. If the Switch variable is false then the output file will contain: --#__COND__ if $Switch --# put_line ("you hit the switch!"); --# put_line ("so these two lines will be printed"); --#__COND__ endif Since the "if" and "elsif" conditions are Perl conditionals, they can be a compound conditional. Use "&&" for AND, "||" for OR, etc. For example: --#__COND__ if ($green && ($apple || $pear)) In addition, the "if ... endif" blocks can be nested. In reality, it is legal to have white-space between the "--#" and the "__COND__" portions of a conditional statement. =item StripPreserve This option will cause all lines beginning with the Conditional Comment prefix ("--#" or its replacement) on the output side of the logic to be replaced with a blank line in the output stream. =item StripDelete This option will cause all lines beginning with the Conditional Comment prefix ("--#" or its replacement) on the output side of the logic to be deleted, that is, not written to the output stream. =item CMNT="comment-string" This option will replace the Conditional Comment string "--#" with the "comment-string" provided. Thus -CMNT="--$$$" would specify an alternative for conditional comment string for Ada, while -CMNT="#*#*" could be used to specify a conditional comment string when preparsing Perl source code. =item CNDT="conditional-string" This option will replace the Conditional string "__COND__" with the "conditional-string" provided. This is the string that comes after the CMNT string to specify a line with "if", "elsif", "else" and "endif" statements. =back =head1 EXAMPLES =head1 BUGS AND CAVEATS Option Flags -CMNT and -CNDT msy not work when calling from another Perl script. (Workaround: just make a separate copy of the pgm that has the desired defaults.) =head1 PREREQUISITES This package should run on standard Perl 5 installations, but may not run on earliest Perl 5 releases or incomplete installations. =head1 STATUS AND SUPPORT This release of Devel::PreProcess is intended primarily for limited private use. Not all options (especially "caller" options) have been tested. =head1 AUTHORS AND COPYRIGHT This program was adapted from a Devel::PreProcessor.pm from Evolution Online Systems, Inc. Ewww.evolution.comE, which had Copyright restrictions on it. It is felt that the changes to it are significant enought that the Copyright is not applicable. Nevertheless, credit is given to them for the basic program structure. Also, an email from them said it is OK to use it in this way. The modifications were made by Peter Richtmyer, RISYS, Portsmouth, RI. Others may use and modify it, just don't sell it or package it for sale. ============================ BAT_PREP.BAT ================================ echo off REM BAT_PREP.BAT REM 03/26/01 REM 04/12/01 added .txt files for help template REM 11/07/01 added logic to create output dir if needed REM 06/08/03 chgd ".scr" to ".script" REM REM need to call this with at least two parameters: REM REM 1 = sending directory REM 2 = receiving directory REM 3 - 9 = (optional) the Perl variable names for preprocessing. REM REM for example: REM REM bat_prep D:\my_dir D:\my_dir2 -demo -gnat REM REM remember we do not WANT TO COPY .BAT FILES if not exist %1 echo #### ERROR in BAT_PREP.BAT, the Sending dir does not exist: %1 #### if not exist %1 pause call CREATE_DIR_IF_NEEDED %2 echo ABOUT TO COPY ALL .ada, .adb, .ads, .script, .txt gnat.* FROM %1 TO %2 CTL-C to QUIT pause rem the options like "-demo" must have a dash before them rem from %1 to %2 with booleans: %3 %4 %5 %6 %7 %8 %9 rem ARE YOU SURE? BACK UP FIRST????? rem echo about to copy all %1\*.txt to %2 rem pause copy %1\*.txt %2 rem echo about to copy all %1\*.script to %2 rem pause copy %1\*.script %2 rem echo about to copy all %1\gnat.* to %2 rem pause copy %1\gnat.* %2 PAUSE for %%f in (%1\*.ada %1\*.ads %1\*.adb) do call bat_chk_convert.bat %%f %2 %3 %4 %5 %6 %7 %8 %9 pause -- check results ========================= BAT_CHK_CONVERT.BAT ========================== echo off REM BAT_CHK_CONVERT.BAT REM 03/26/01 REM REM need to call this with at least two parameters: REM REM 1 = sending directory REM 2 = receiving directory REM 3 - 9 = (optional) the Perl variable names for preprocessing. REM REM for example: REM REM bat_chk_convert D:\my_dir D:\my_dir2 -demo -gnat REM rem the options like "-demo" must have a dash before them rem from %1 to %2 with booleans: %3 %4 %5 %6 %7 %8 %9 rem Note that we only want to preprocess if we have to. rem otherwise we use a "copy" command so the file date does not change. rem pause rem first see if there are any conditionals ("--#") in the file rem echo calling PreCheck.pm %3 %4 %5 %6 %7 %8 %9 %1 perl -s C:\perl\bin\PreCheck.pm %3 %4 %5 %6 %7 %8 %9 %1 if not errorlevel 1 goto copyfile rem echo PreProcess.pm %3 %4 %5 %6 %7 %8 %9 %1 %2 perl -s C:\perl\bin\PreProcess.pm %3 %4 %5 %6 %7 %8 %9 %1 %2 goto end :copyfile copy %1 %2 :end rem pause ======================= CREATE_DIR_IF_NEEDED.BAT ======================= echo off REM CREATE_DIR_IF_NEEDED.BAT REM REM 11/07/01 new REM REM need to call this with parameters REM REM 1 = directory to check, and add if it does not exist REM REM for example: REM REM call CREATE_DIR_IF_NEEDED D:\my_dir REM if not exist %1 echo Creating the output dir: %1 if not exist %1 mkdir %1 if not exist %1 echo #### ERROR: could not create output dir: %1 #### if not exist %1 pause