#!/usr/bin/perl ############################################################################### # # # Name : template # # Author : Chris Koeritz # # Rights : Copyright (C) 1996-$now by Author # # # # Purpose: # # # # Attempts to pre-instantiate C++ templates to work-around C++ compilers # # that don't support templates (a rare breed, these days). # # # ############################################################################### # 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 2 of the License or (at your option) # # any later version. See: "http://www.gruntose.com/Info/GNU/GPL.html" for a # # version of the License. Please send any updates to "fred@gruntose.com". # ############################################################################### # this was a majestic abortive attempt to create a template instantiator for # compilers that do not possess templates, but which do support some subset # of C++. This was necessary at the time, due to our firmware compiler's # limitations. This processor never totally worked, although it did produce # some interesting compilable code. Might be useful as a demo or maybe just # as a warning to avoid brain-damaged C++ compilers. # to do: # maintain statistics about placement in file for error resolution. # limitations so far: # # the word "template" must be the first word on the line. # # the type to instantiate must be one word (like charstar, not char *). # # templates must follow the form templateName without # any spaces between the angle brackets. # flag to enable debugging print outs. $DEBUG_TEMPLATIZER = 1; #$DEBUG_TEMPLATIZER = 0; # array to store instance types to convert to @instance_type_buffer = (); # flag for checking read from file option $f_option = 0; $d_option = 0; if ($#ARGV < 1) { die(" The template instantiater supports an optional directory path as the first parameter (preceded by a -d with no spaces in between the d and the directory name) in which to store the generated templates, and then requires the instantiation type as the next argument (or a file specification preceded by -f), and a list of files as the last arguments. The files will be scanned for templates and those templates will be instantiated in the type(s) specified. Examples: perl template.pl char istring.h torpedo.h perl template.pl -f instance.txt matrix.h perl template.pl -d. -f instance.txt function.h data_src.h perl template.pl -dfirm_src\library\basis -f instance.txt amorph.h\n"); } # Check directory option if (grep(/^\s*-d/, @ARGV)) { $d_option = 1; $d_dir = @ARGV[0]; # print $d_dir, "\n"; shift; } # Check to see if user used a file to specify instantiation types if (grep(/^\s*-f/, @ARGV)) { $f_option = 1; shift; $types_file = @ARGV[0]; # Open instantiation type file to read from open(TYPES_FILE, "<$types_file") || die("couldn't open file $types_file for reading"); # Read in all the different types to instantiate # Create instance_type list @tp = ; while (@tp) { local($line) = @tp; chop $line; push(@instance_type_buffer, $line); shift @tp; } shift @ARGV; &instantiate_templates(@ARGV); exit; } &instantiate_templates(@ARGV); exit; # # driver of the instantiation process. # sub instantiate_templates { if (!$f_option) { # grab the user's desired instance type. $instance_type = @_[0]; push(@instance_type_buffer, $instance_type); print "Instantiation type is \"$instance_type\".\n"; # jump over the instance type to look at the filenames. shift; } local($i) = 0; foreach $filename (@_) { open(INPUT_FILE, "<$filename") || die("couldn't open file $filename for reading"); # create an output name for the instance. $out_filename = &make_output_name($filename); if ($DEBUG_TEMPLATIZER) { # print "out file is ", $out_filename, "\n"; } local($index) = $i + 1; print "Instantiating file[$index] as $out_filename.\n"; # now try opening our output file. open(OUTPUT_FILE, ">$out_filename") || die("couldn't open file $filename for writing"); # grab the current file into an array. @file_array = ; @start_template = @file_array; @stop_template = @file_array; # process the file's contents as a manipulable array. while (@file_array) { local($line) = shift @file_array; if (grep(/^\s*template/, $line)) { @start_template = @file_array; # iterate through all the instance types for each template foreach $instance_type (@instance_type_buffer) { @file_array = @start_template; &snag_place_holder($line); &snag_object_name; &absorb_until_matched_block; &replace_place_holder; &dump_the_buffer; print OUTPUT_FILE "\n"; } } elsif (grep(/\w+<\w+>/, $line)) { local(@pieces) = split(/\s/, $line); foreach $piece (@pieces) { local($prefix) = ""; # special case for separating function name from templated first # parameter to it. if (grep(/\(\w+/, $piece)) { $piece = &special_mangle($piece); } print OUTPUT_FILE "$prefix$piece "; } print OUTPUT_FILE "\n"; } else { print OUTPUT_FILE $line; } } $i++; } } # # generates an output name from the filename to be translated. # sub make_output_name { local($out_filename) = @_[0]; local($d_dir_temp) = $d_dir; # print "OUTFILE NAME: ",$out_filename,"\n"; # break down the filename at the slashes. local(@split_filename) = split(/[\\\/]/, $out_filename); # take the basename of the list of names. $out_filename = $split_filename[$#split_filename]; local($hold_filename) = $out_filename; if (grep(!/\.cpp$/i, $out_filename) && grep(!/\.h$/i, $out_filename) && grep(!/\.c$/i, $out_filename) && grep(!/\.h$/i, $out_filename) ) { die("filename @_[0] not recognized as a C++ code file."); } # makes an instance of the file in a directory named after the instance type # that is located under the current directory. $d_dir_temp = join('/',$d_dir, $hold_filename); if ($d_option) { $d_dir_temp =~ s/-d//i; @split_filename = split(/[\\\/]/, $d_dir_temp); # exit; } # try to create dir using the deepest dir given in filename input local($y) = 0; foreach (@split_filename) { $y++; } local($x) = 0; local($ret) = 0; local($dirs) = 0; if ($y >= 2) { foreach (@split_filename) { if ((($x > 0) && ($x < $y-1)) || (($d_option) && ($x < $y-1))) { if (!$dirs) { $dirs = @split_filename[$x]; } else { $dirs = $dirs."/".@split_filename[$x]; } # print "Creating... ",$dirs,"\n"; $ret = mkdir($dirs, 0777); if (!ret) { die("a directory named $instance_dir could not be made."); } } $x++; } $out_filename = $dirs."/".$hold_filename; } else { $out_filename = "template/".$hold_filename; local($instance_dir) = "template"; $ret = mkdir($instance_dir, 0777); if (!ret) { die("a directory named $instance_dir could not be made."); } } # print $out_filename, "\n"; # local($instance_dir) = @split_filename[$x-2]; # creates the directory. # local($ret) = mkdir($instance_dir, 0777); # if (!ret) { die("a directory named $instance_dir could not be made."); } $out_filename; # return the new name. } # # grabs the name of the placeholder type that will be replaced by # the template instantiation type. # sub snag_place_holder { $place_holder = @_[0]; chop $place_holder; local(@pieces) = split(/>\s*/, $place_holder, 2); # send back the parts not involved in the template statement. if (length($pieces[1])) { unshift(@file_array, $pieces[1]."\n"); } $place_holder = $pieces[0]; $place_holder =~ s/\s*template\s+"])/\1 \2 \3/; local(@broken_up) = split(/ /, $temp); # strip off the punctuation from the name. local($incl_prefix) = @broken_up[1]; $incl_prefix =~ s/["<](.*)[">]/\1/; $incl_prefix =~ s/\s//g; # return if it's not a code file being included. if (!grep(/.cpp/i, $incl_prefix)) { print OUTPUT_FILE $line, "\n"; return; } # strip to just the name without the ending. $incl_prefix =~ s/\.cpp$//i; # now get the name of the file we're processing. local($file_prefix) = $filename; # return if it's not a header file being examined. if (!grep(/.h/i, $file_prefix)) { print OUTPUT_FILE $line, "\n"; return; } # strip off the extension. $file_prefix =~ s/\.h$//i; # return if the names aren't equivalent--this means the include doesn't # refer to our new templated form of the code file. if ($incl_prefix ne $file_prefix) { print OUTPUT_FILE $line, "\n"; return FALSE; } # dump out a message about the removal. $line =~ s/^\s*//; print OUTPUT_FILE "/* removed unneeded template inclusion: $line */\n"; } # # extracts lines from the file until the curly brackets are matched up # at level 0. # sub absorb_until_matched_block { $bracket_level = 0; $end_absorb = 0; @template_buffer = (); $hit_one=0; while (@file_array) { local($line) = shift @file_array; &look_for_curlies($line); if (($hit_one && ($bracket_level == 0)) || $end_absorb) { return; } } } # # examines the parameters passed in for curly brackets and changes the # counter if they are found. # sub look_for_curlies { # $hit_one = 0; # records whether a bracket was found or not. local($line) = @_[0]; @word = (); foreach $char (split(//, $line)) { if ($char eq '{') { $hit_one = 1; $bracket_level++; } elsif ($char eq '}') { $hit_one = 1; $bracket_level--; } elsif (($char eq ';') && ($hit_one==0)) { $end_absorb = 1; } if ($DEBUG_TEMPLATIZER) { # print "~$char~ "; } push(@word, $char); if (grep(!/\w/, $char)) { # don't split yet if it's a possible template char. if (grep(!/[<>]/, $char)) { local($real_word) = join("", @word); if ($DEBUG_TEMPLATIZER) { # print "adding a word $real_word\n"; } push(@template_buffer, "$real_word"); @word = (); } } } } # # this goes through the buffer and replaces all occurrences of the name to # replace with the instance name. # sub replace_place_holder { @new_template_buffer = @template_buffer; @template_buffer = (); foreach $i (0 .. $#new_template_buffer) { $word = $new_template_buffer[$i]; # if ($DEBUG_TEMPLATIZER) { # print "<$i $word> "; # $old = $word; # } # replace a templated combination with the mangled version. $word =~ s/^${object_name}<${instance_type}>/${mangled_name}/; # if ($DEBUG_TEMPLATIZER) { # if ($old ne $word) {print "1 ... changed to $word.\n"; $old = $word; } # } if (grep(/^\w+<\w+>/, $word)) { # replace some other template with our stuff if we can. $word = &special_mangle($word); } # if ($DEBUG_TEMPLATIZER) { # if ($old ne $word) {print "2 ... changed to $word.\n"; $old = $word; } # } # replace the object's name with its mangled form. $word =~ s/^${object_name}/${mangled_name}/; # if ($DEBUG_TEMPLATIZER) { # if ($old ne $word) {print "3... changed to $word.\n"; $old = $word; } # } # replace the place holder with the instantiation type. $word =~ s/^${place_holder}/${instance_type}/; # if ($DEBUG_TEMPLATIZER) { # if ($old ne $word) {print "4... changed to $word.\n"; $old = $word; } # } push(@template_buffer, $word); } } # # processes a general template usage, in the form X, where either # X or Y are not ones that we think we need to replace. it is assumed # that it's safe to use the mangled form of the template. # sub special_mangle { local($word) = @_[0]; # split the template form into pieces. local(@pieces) = split(/[<>]/, $word, 2); $pieces[1] =~ s/${place_holder}/${instance_type}/; $pieces[1] =~ s/>//; # hold onto the real instance type. local($hold_instance) = $instance_type; $instance_type = $pieces[1]; # mangle the name in the template usage line. local($hold_mangled) = &mangle_name($pieces[0]); # restore the original instance type. $instance_type = $hold_instance; # returns the new mangled form. $hold_mangled; } # # prints out the buffer we've accumulated to the output file. # sub dump_the_buffer { print OUTPUT_FILE @template_buffer; } # # processes a class declaration and sets the object name for future use. # sub match_class_declaration { local($next_line) = @_[0]; # too strict! # if (grep(!/class\s.*\w+$/, $next_line) # && grep(!/class\s.*\w+\s*\{/, $next_line) # && grep(!/struct\s.*\w+$/, $next_line) # && grep(!/struct\s.*\w+\s*\{/, $next_line)) { # return 0; # } if (grep(!/class\s+\w+/, $next_line) && grep(!/struct\s+\w+/, $next_line) ) { return 0; } if ($DEBUG_TEMPLATIZER) { # print "matched class decl in $next_line\n"; } if (grep(/class\s+\w+.*:/, $next_line) || grep(/struct\s+\w+.*:/, $next_line)) { # parses an inheriting class decl. if ($DEBUG_TEMPLATIZER) { # print "in inheritance case on $next_line\n"; } local(@pieces) = split(/:/, $next_line, 2); # push the rest of the line back into the input array. if ($DEBUG_TEMPLATIZER) { # print "going to unshift $pieces[1]...\n"; } unshift(@file_array, ": ".$pieces[1]." "); $next_line = $pieces[0]; } elsif (grep(/class\s.*\w+\s*\{/, $next_line) || grep(/struct\s.*\w+\s*\{/, $next_line)) { # parses a non-inheriting declaration with bracket on same line. if ($DEBUG_TEMPLATIZER) { # print "in special case on $next_line\n"; } # special case for continued stuff on same line. local(@pieces) = split(/{/, $next_line, 2); # push the rest of the line back into the input array. unshift(@file_array, " { ".$pieces[1]." "); $next_line = $pieces[0]; } if ($DEBUG_TEMPLATIZER) { # print "matched class declaration... $next_line\n"; } local(@pieces) = split(/\s/, $next_line); $object_name = $pieces[$#pieces]; $mangled_name = &mangle_name($object_name); foreach $posn (0 .. $#pieces - 1) { print OUTPUT_FILE "$pieces[$posn] "; } print OUTPUT_FILE "$mangled_name\n"; 1; } # # processes the implementation of a class member and sets the object # name for future use. # sub match_class_member_definition { local($next_line) = @_[0]; local($junk); if (grep(!/\w+<\w+>::/, $next_line)) { return 0; } if ($DEBUG_TEMPLATIZER) { # print "matched class member definition... $next_line\n"; } local(@pieces) = split(/>::/, $next_line, 2); # checks for spaces in the first part of the split. if there is one, # it means we don't have a simple object thing. if (grep(/\s/, $pieces[0])) { if ($DEBUG_TEMPLATIZER) { # print "matched a space in the first part of supposed object name... $pieces[0]\n"; } if (grep(/^\w+<\w+>/, $pieces[0])) { if ($DEBUG_TEMPLATIZER) { # print "matched a template usage in first part of name..."; } # replace some other template with our stuff if we can. $pieces[0] = &special_mangle($pieces[0]); } if ($DEBUG_TEMPLATIZER) { # print "now our first bit is: $pieces[0]\n"; } local(@new_pieces) = split(/ /, $pieces[0]); $pieces[0] = $new_pieces[$#new_pieces]; foreach $posn (0 .. $#new_pieces - 1) { $new_pieces[$posn] =~ s/${place_holder}/${instance_type}/g; print OUTPUT_FILE "$new_pieces[$posn] "; } } unshift(@file_array, "::\n".$pieces[1]."\n"); $object_name = $pieces[0]; $object_name =~ s/(\W*)(\w+)<(\w+)/\2/; if (length($1)) { print OUTPUT_FILE "$1"; } if ($3 ne $place_holder) { die("The placeholder does not match on this line: $next_line"); } $mangled_name = &mangle_name($object_name); print OUTPUT_FILE "$mangled_name\n"; 1; # return success. } # # processes a function template by making sure it fits the format and # then setting up the variables for the replacement. since function templates # are so simple, the object name is not changed; only the place_holder is # changed to the instance type. # sub match_function_definition { local($next_line) = @_[0]; if (grep(!/^\s*\w+\s+.*/, $next_line) ) { if ($DEBUG_TEMPLATIZER) { print "failed on funcdef for ", $next_line, "!\n"; } return 0; } # old broken code:... # if (grep(!/^\s*\w+\s+.*\(.*\)\s*/, $next_line) ) { #print "failed on funcdef for ", $next_line, "!\n"; # return 0; # } # if ($DEBUG_TEMPLATIZER) { # print "matched function definition on $next_line\n"; # } # if ($DEBUG_TEMPLATIZER) { # print "stuffing back into the file array $next_line.\n"; # } # put the line back because it's nearly right for being instantiated. unshift(@file_array, "inline ".$next_line."\n"); # come up with a very rare name that will not be matched in the text. $object_name = "hogga_wogga_nunky_budget_weeny_teeny_kahini_beany"; $mangled_name = &mangle_name($object_name); 1; # return a success. }