summaryrefslogtreecommitdiff
path: root/ACE/bin/FOCUS/Parser/FOCUSParser.pm
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/bin/FOCUS/Parser/FOCUSParser.pm')
-rw-r--r--ACE/bin/FOCUS/Parser/FOCUSParser.pm726
1 files changed, 726 insertions, 0 deletions
diff --git a/ACE/bin/FOCUS/Parser/FOCUSParser.pm b/ACE/bin/FOCUS/Parser/FOCUSParser.pm
new file mode 100644
index 00000000000..b2061a660a4
--- /dev/null
+++ b/ACE/bin/FOCUS/Parser/FOCUSParser.pm
@@ -0,0 +1,726 @@
+#########################################################################
+# A Simple Parser for automating the specializations crated in FOCUS.
+#
+# @author Arvind S. Krishna <arvindk@dre.vanderbilt.edu>
+#
+# $Id$
+#
+# This parser, parses the specialization file given as an input argument
+# and *individually* visits the tags in a pre-determined order to weave
+# in the specializations.
+# NOTE: This parser will make N passes over the file, where N equals
+# to the number of tags defined in the specialization file. This
+# approach is intentional as it servers current needs. Future versions
+# may enhance this parser and Visit methods to be more intelligent.
+###########################################################################
+package FOCUSParser;
+
+# for MY own preferences!
+use strict;
+
+# XML related operations
+use XML::DOM;
+
+# Generic file operations
+use FileHandle;
+
+# Creating files and renaming them
+use File::Copy;
+
+# Creating directories
+use File::Path;
+
+############################################
+# GLOBAL CONSTANTS
+###########################################
+my $FOCUS_PREPEND_TAG = "\/\/@@ ";
+
+####################################################################
+# banner: A function that returns the FOCUS banner transformation
+# for just clarity purpose only.
+###################################################################
+sub FOCUS_banner_start
+{
+ my $banner_str = "// Code woven by FOCUS:\n";
+ return $banner_str;
+}
+
+sub FOCUS_banner_end
+{
+ my $banner_str = "// END Code woven by FOCUS\n";
+ return $banner_str;
+}
+
+#########################################################################
+# Visit_ADD: Visit a add element defined in the transform.
+# In particular look for the hook defined: search it in the source file
+# and add the data in the <data> tags into the file starting from the
+# hook, but not including the hook.
+##########################################################################
+sub Visit_Add
+{
+ my ($add, $copy_file_name) = @_;
+
+ # Open the copy and transform it
+ open (IN, "+<". $copy_file_name) ||
+ die "cannot open file: " . $copy_file_name;
+
+ # To update a file in place, we use the temporary
+ # file idiom. Perl says this is the best way to
+ # do this!
+ my $copy_file_tmp = $copy_file_name . "tmp";
+ open (OUT, ">". $copy_file_tmp) ||
+ die "cannot open temporary file for modying file:" . $copy_file_name;
+
+ # get the hook element defined in the add element
+ my $hook = $add->getElementsByTagName ('hook');
+
+ # ensure length of hook == 1;
+ if ($hook->getLength != 1)
+ {
+ print "Assertion Error: An <add> element can have only \
+ one <hook> definition";
+
+ # clean up
+ close (IN);
+ close (OUT);
+
+ # Diagnostic comment
+ print " [failure]... Reverting changes \n";
+
+ unlink ($copy_file_name);
+ unlink ($copy_file_name . "tmp");
+ exit (1);
+ }
+
+ # Check if the hook is present in the file at all
+ my $hook_str = $hook->item(0)->getFirstChild->getNodeValue;
+ chomp ($hook_str);
+
+ #//@@ For now, due to problem with the hook string
+ my $search_str = $hook_str;
+
+ while (<IN>)
+ {
+ if (/$search_str/)
+ {
+ # Do not remove the hook! It needs to be present
+ print OUT $_;
+
+ # FOCUS banner start
+ print OUT FOCUS_banner_start;
+
+ # parse <data> ... </data> elements for this add tag
+ my @data_list = $add->getElementsByTagName ('data');
+ foreach my $data (@data_list)
+ {
+ my $data_item = $data->getFirstChild->getNodeValue;
+ chomp ($data_item);
+
+ # Insert the item
+ print OUT "$data_item \n";
+ }
+
+ # FOCUS banner end
+ print OUT FOCUS_banner_end;
+ }
+ else
+ { print OUT $_; }
+ }
+
+ # Everything went well!
+ close (IN);
+ close (OUT);
+
+ # replace in place the old file with the new one
+ rename ($copy_file_tmp, $copy_file_name);
+}
+
+###########################################################################
+# Visit_Remove: Visit a <remove> element defined in the transform.
+# In particular look for the hook defined: search it in the source file
+# and remove the element's value from the source file being searched.
+############################################################################
+sub Visit_Remove
+{
+ my ($remove, $copy_file_name) = @_;
+
+ # obtain the data to be removed
+ my $search = $remove->getFirstChild->getNodeValue;
+ chomp ($search);
+
+ # Open the copy and transform it
+ open (IN, "+<" . $copy_file_name) ||
+ die "cannot open file: " . $copy_file_name;
+
+ # Update the file in place
+ my $copy_file_name_tmp = $copy_file_name . "tmp";
+ open (OUT, ">". $copy_file_name_tmp) ||
+ die "cannot open temporary file for modying file:" . $copy_file_name;;
+
+ # Removing something is same as search and replace. Replace with ""
+ my $replace = "";
+
+ foreach my $line (<IN>)
+ {
+ if ($line =~/$search/)
+ {
+ # We do not print the banner information
+ # as we have removed something and
+ # print the banner will be redundant!
+
+ # replace <search> with <replace>
+ $line =~ s/$search/$replace/;
+
+ print OUT $line;
+ }
+ else { print OUT $line; }
+ }
+
+ # Everything went well!
+ close (IN);
+ close (OUT);
+
+ # replace in place the old file with the new one
+ rename ($copy_file_name_tmp, $copy_file_name);
+}
+
+#########################################################################
+# Visit_Substitute: Visit a <substitute> element defined in the transform.
+# In particular look for the <search> element and replace it with the
+# <replace> element.
+#########################################################################
+sub Visit_Substitute
+{
+ my ($substitute, $copy_file_name) = @_;
+
+ # Open the copy and transform it
+ open (IN, "+<". $copy_file_name) ||
+ die "cannot open file: " . $copy_file_name;
+
+ # To update a file in place, we use the temporary
+ # file idiom. Perl says this is the best way to
+ # do this!
+ my $copy_file_name_tmp = $copy_file_name . "tmp";
+ open (OUT, ">". $copy_file_name . "tmp") ||
+ die "cannot open temporary file for modying file:" . $copy_file_name;;
+
+ # check if the match-line keyword is set or not
+ my $match_line = $substitute->getAttribute('match-line');
+
+ # <search> .... </search>
+ my $search_list = $substitute->getElementsByTagName ('search');
+
+ # ensure length of search == 1;
+ if ($search_list->getLength != 1 ||
+ $search_list->getLength == 0)
+ {
+ print "Assertion Error: A <substitute> element can have only \
+ one <search> element";
+ close (IN);
+ close (OUT);
+
+ # Dianostic comment
+ print " [failure] reverting changes \n";
+
+ unlink ($copy_file_name);
+ unlink ($copy_file_name_tmp);
+ exit (1);
+ }
+
+ # <replace> .... </replace>
+ my $replace_list = $substitute->getElementsByTagName ('replace');
+ if ($replace_list->getLength != 1 ||
+ $replace_list->getLength == 0)
+ {
+ print "Assertion Error: A <substitute> element can have only \
+ one <replace> element";
+ close (IN);
+ close (OUT);
+ unlink ($copy_file_name);
+ unlink ($copy_file_name_tmp);
+ exit (1);
+ }
+
+ # <search> and <replace> element values
+ my $search = $search_list->item(0)->getFirstChild->getNodeValue;
+ my $replace = $replace_list->item(0)->getFirstChild->getNodeValue;
+
+ # remove spaces
+ chomp ($search);
+ chomp ($replace);
+
+ # Search and replace string in the file
+ foreach my $line (<IN>)
+ {
+ # Check if the match line attribute is set. If so then
+ # ignore word boundaries. If not, honor word boundaries.
+ my $line_matched = 0;
+ if (! $match_line)
+ {
+ if ($line =~/\b$search\b/)
+ {
+ $line_matched = 1;
+ }
+ }
+ else
+ {
+ if ($line =~ /$search/)
+ {
+ $line_matched = 1;
+ }
+ }
+
+ # Check if the line matched
+ if ($line_matched)
+ {
+ # FOCUS banner start
+ print OUT FOCUS_banner_start;
+
+ # replace <search> with <replace>
+ # Caveat: What if <search> occures multiple
+ # times in the line? Here is how we handle
+ # it
+ $line =~ s/$search/$replace/g;
+
+ print OUT $line;
+
+ # FOCUS banner end
+ print OUT FOCUS_banner_end;
+ }
+ else { print OUT $line; }
+ }
+
+ # everything went well!
+ close (IN);
+ close (OUT);
+
+ # replace in place the old file with the new one
+ rename ($copy_file_name_tmp, $copy_file_name);
+}
+
+#########################################################################
+# Visit_Comment: Visit the comment-region hooks defined in the
+# source code and comment out all code between start and finish of that
+# region
+#########################################################################
+sub Visit_Comment
+{
+ my ($comment, $copy_file_name) = @_;
+
+ # check for the comment region tags and
+ # comment out the region
+ my $start_hook_tag = $comment->getElementsByTagName ('start-hook');
+ my $end_hook_tag = $comment->getElementsByTagName ('end-hook');
+
+ if ($start_hook_tag->getLength != 1 ||
+ $end_hook_tag->getLength != 1)
+ {
+ print "Assertion Error: A <comment> element can have only \
+ one pair of <start-hook> and <end-hook> tags";
+ unlink ($copy_file_name);
+ exit (1);
+ }
+
+ my $start = $start_hook_tag->item(0)->getFirstChild->getNodeValue;
+ my $end = $end_hook_tag->item(0)->getFirstChild->getNodeValue;
+
+ # What are we looking for:
+ # We need to start from "//" . FOCUS_PREPEND_TAG . $hook
+ # i.e. //[[@ <blah blah>
+ # This will be the format for both start and end
+ # //@@ Problems with the hook string
+ my $start_hook = $FOCUS_PREPEND_TAG . $start;
+ my $end_hook = $FOCUS_PREPEND_TAG . $end;
+
+ # Open the copy and transform it
+ open (IN, "+<". $copy_file_name) ||
+ die "cannot open file: " . $copy_file_name;
+
+ my $copy_file_name_tmp = $copy_file_name . "tmp";
+ open (OUT, ">". $copy_file_name_tmp) ||
+ die "cannot open temporary file for modying file:" . $copy_file_name;
+
+ my $start_commenting = 0;
+ while (<IN>)
+ {
+ if (! /$start_hook/ &&
+ ! /$end_hook/)
+ {
+ if ($start_commenting)
+ { print OUT "// " . $_; }
+ else
+ { print OUT $_; }
+ }
+ else
+ {
+ if (/$start_hook/)
+ {
+ $start_commenting = 1;
+ print OUT $_; # print start hook!
+ }
+ else
+ {
+ $start_commenting = 0;
+ print OUT $_; # print end hook!
+ }
+ }
+ }
+
+ # everything went well!
+ close (IN);
+ close (OUT);
+
+ rename ($copy_file_name_tmp, $copy_file_name);
+}
+
+###############################################################
+# Visit_Copy: visit the <copy> tags and weave the code into the
+# source file. In particular, open the source file specified
+# in the file-source tag. Search for the start hook and
+# copy until the end hook is reached.
+###############################################################
+sub Visit_Copy
+{
+ my ($copy_tag, $copy_file_name, $default_module_name, $prefix_path) = @_;
+
+ # Check if a file name has been specified
+ my $dest_file_tag = $copy_tag->getElementsByTagName ('source');
+
+ if (! $dest_file_tag)
+ {
+ print "Error: <copy-from-source> does not have the <file> tag..";
+ print "aborting \n";
+ exit 1;
+ }
+
+ if ($dest_file_tag->getLength != 1)
+ {
+ print "Assertion Error: A <copy-from-source> element can have only \
+ one <source> tag from which to copy elements";
+ exit (1);
+ }
+
+ my $dest_file_name = $dest_file_tag->item(0)->getFirstChild->getNodeValue;
+
+ #Check if the file exists and one is able to access it
+ $dest_file_name = $prefix_path . "/" . $default_module_name . "/" . $dest_file_name;
+
+ open (DEST, "<". $dest_file_name) ||
+ die "cannot open $dest_file_name \n Wrong <file> tag within <copy-from-source> exiting" ;
+
+ # check for the start and end tags within the target file where
+ # one needs to start copying from
+ my $start_tag = $copy_tag->getElementsByTagName ('copy-hook-start');
+ my $end_tag = $copy_tag->getElementsByTagName ('copy-hook-end');
+
+ if (! $start_tag || ! $end_tag)
+ {
+ print "Assertion Error: A <copy> element should have a \
+ <copy-hook-start> tag and <copy-hook-end> tag \n";
+ exit (1);
+ }
+
+ # Get the <dest-hook> tag that indicates the destination where the
+ # code between the start and end tags will be placed.
+ my $dest_hook_tag = $copy_tag->getElementsByTagName ('dest-hook');
+ if (! $dest_hook_tag)
+ {
+ print "Assertion Error: <copy-from-source> should have a <dest-hook> \
+ tag that dictates where in the source file the code should be \
+ placed. \n";
+ exit (1);
+ }
+
+ # Remove any starting and trailing white spaces
+ chomp ($dest_hook_tag);
+
+ # We have everything we need! Do the copy
+ my $start_tag_name = $start_tag->item(0)->getFirstChild->getNodeValue;
+ my $end_tag_name = $end_tag->item(0)->getFirstChild->getNodeValue;
+ my $dest_tag_name = $dest_hook_tag->item(0)->getFirstChild->getNodeValue;
+
+ # First we add the FOCUS prepend tags
+ $start_tag_name = $FOCUS_PREPEND_TAG . $start_tag_name;
+ $end_tag_name = $FOCUS_PREPEND_TAG . $end_tag_name;
+ $dest_tag_name = $FOCUS_PREPEND_TAG . $dest_tag_name;
+
+ # Step 1: Iterate over the target file till the
+ # dest-hook is found in that file
+ my $copy_file_name_tmp = $copy_file_name . "tmp";
+ open (OUT, ">". $copy_file_name_tmp) ||
+ die "cannot open temporary file for modying file:" . $copy_file_name;
+ open (IN, "<" . $copy_file_name) ||
+ die "cannot open file $copy_file_name specified in the <file> tag \n";
+
+ my $dest_tag_found = 0; #check if tag matched
+ foreach my $line (<IN>)
+ {
+ if ($line =~ /$dest_tag_name/)
+ { $dest_tag_found = 1; print OUT $line; last; }
+
+ print OUT $line;
+ }
+ close (IN);
+
+ # If we reached the end of file before finding the tag!
+ if (! $dest_tag_found)
+ {
+ print "\n Error: <dest-hook> tag missing in file .. aborting \n";
+ close (DEST);
+ close (IN);
+ close (OUT);
+ unlink ($copy_file_name_tmp);
+ exit (1);
+ }
+
+ # Step 2: Now look in the destination file and look for the hooks
+ # where one needs to copy. There could be multiple places where the
+ # hook can be present. E.g.
+ # .......
+ # //@@ COPY_START_HOOK
+ # ....
+ # ....
+ # //@@ COPY_END_HOOK
+ # ....
+ # ....
+ # //@@ COPY_START_HOOK
+ # ....
+ # ....
+ # //@@ COPY_END_HOOK
+ # Handle this case
+
+ my $line_matched = 0;
+ my $start_copying = 0; # initially do not copy
+ foreach my $line (<DEST>)
+ {
+ # Check if the line matches the start tag
+ if ($line =~/$start_tag_name/)
+ {
+ $line_matched += 1;
+ $start_copying = 1;
+ }
+ else
+ {
+ # Check if the line matches the end tag
+ if ($line =~/$end_tag_name/)
+ {
+ # check if the start tag matched!
+ if (! $line_matched)
+ {
+ print "Assertion error: <copy-hook-end> tag misplaced with \
+ the <copy-hoook-source> \n";
+ close (DEST);
+ close (IN);
+ close (OUT);
+ unlink ($copy_file_name_tmp);
+ exit (1);
+ }
+
+ # decrement the count for nested tags
+ $line_matched -= 1;
+ if (! $line_matched )
+ { $start_copying = 0; }
+ }
+ else
+ {
+ # Print out the line
+ if ($start_copying)
+ { print OUT $line; }
+ }
+ }
+ }
+
+ # At the end of this loop line_matched should be 0
+ if ($line_matched)
+ {
+ print "Error: in $dest_file_name, number of <copy-hook-source> tags \
+ did not match the number of <copy-hook-end> tags. Reverting \
+ changes. \n";
+ close (DEST);
+ close (IN);
+ close (OUT);
+ unlink ($copy_file_name_tmp);
+ exit (1);
+ }
+
+ # Step 3: Now copy data after the tag in the original file onto the destination
+ # file.
+ open (IN, "<" . $copy_file_name) ||
+ die "cannot open file $copy_file_name specified in the <file> tag \n";
+ $dest_tag_found = 0; #used as a flag
+ foreach my $line (<IN>)
+ {
+ if ($dest_tag_found)
+ { print OUT $line; }
+
+ # If the hook is found, then don't write the hook onto OUT
+ # as it would have been written earlier
+ if (! $dest_tag_found &&
+ $line =~ /$dest_tag_name/)
+ { $dest_tag_found = 1; }
+ }
+
+ # Normal exit path
+ close (IN);
+ close (OUT);
+ close (DEST);
+
+ # Rename the tmp file to the file modified
+ rename ($copy_file_name_tmp, $copy_file_name);
+}
+
+#################################################################
+# commit_files: A procedure to commit all the copy files that
+# were specialized back to the orginal files.
+#################################################################
+sub commit_files
+{
+ my ($path_name, $output_path_name, @files) = @_;
+
+ # iterate over the file_name_list
+ foreach my $file (@files)
+ {
+ # <file name="....">
+ my $file_name = $file->getAttribute('name');
+
+ # output_path == input_path then do an in place
+ # substitution.
+ if ($output_path_name eq $path_name)
+ {
+ rename ($path_name . "/" . $file_name . "copy",
+ $path_name . "/" . $file_name);
+ }
+ else
+ {
+ # Check if the path_name exists. The path name
+ # corresponds to a directory. So create it if it does
+ # not exist.
+ if (! -d $output_path_name)
+ {
+ #@@? Need to revert the *copy files?
+ mkpath ($output_path_name, 0, 0744) ||
+ die "cannot create $output_path_name: commit files failed! \n";
+ }
+
+ # move the specialized file to the output directory
+ rename ($path_name . "/" . $file_name . "copy",
+ $output_path_name . "/" . $file_name);
+ }
+ }
+}
+
+#### Main ########################################################
+# Specialize_Component
+# procedure to execute the transformations specified in the
+# specialization file
+##################################################################
+sub Specialize_Components
+{
+ # Get the command line arguments
+ my ($prefix_path, $spl_file, $output_prefix) = @_;
+
+ my $parser = XML::DOM::Parser->new();
+ my $doc = $parser->parsefile($spl_file);
+
+ # Check if the prefix path ends with a / or not
+ # if it does not then manually add the / to it
+ my $last = substr ($prefix_path, -1);
+ if ($last ne "/")
+ { $prefix_path = $prefix_path . "/"; }
+
+ # Entry Point: <transform> element
+ foreach my $transform ($doc->getElementsByTagName('transform'))
+ {
+ # <module tags>
+ foreach my $module ($transform->getElementsByTagName('module'))
+ {
+ # Complete path name to the module
+ my $module_name = $module->getAttribute('name');
+ my $path_name = $prefix_path . $module_name;
+
+ # <file tags>
+ my @files = $module->getElementsByTagName('file');
+ foreach my $file (@files)
+ {
+ # <file name="....">
+ my $file_name = $file->getAttribute('name');
+
+ # Rather than modifying the files directly, make a local
+ # copy of the files and then transform them and commit
+ # if there is a file called foo we make a file foo_copy
+ my $file_path_copy = $path_name . "/" . $file_name . "copy";
+ my $file_path_name = $path_name . "/" . $file_name;
+
+ copy ($file_path_name, $file_path_copy);
+
+ # Diagnostic comment
+ print "Instrumenting $file_name ..........";
+
+ # <comment> ... </comment>
+ my @comment_list = $file->getElementsByTagName ('comment');
+ foreach my $comment (@comment_list)
+ { Visit_Comment ($comment, $file_path_copy); }
+
+ # <copy-from-source> ... </copy-from-source>
+ my @copy_from_source_files =
+ $file->getElementsByTagName ('copy-from-source');
+ foreach my $copy_from_source (@copy_from_source_files)
+ {
+ Visit_Copy ($copy_from_source,
+ $file_path_copy,
+ $module_name,
+ $prefix_path);
+ }
+
+ # <remove> ... </remove>
+ my @remove_list = $file->getElementsByTagName ('remove');
+ foreach my $remove (@remove_list)
+ { Visit_Remove ($remove, $file_path_copy); }
+
+ # <substitute ... </substitute>
+ my @substitute_list = $file->getElementsByTagName ('substitute');
+ foreach my $substitute (@substitute_list)
+ { Visit_Substitute ($substitute, $file_path_copy); }
+
+ # <add> <hook> ...... </hook> <add>
+ my @add_list = $file->getElementsByTagName ('add');
+ foreach my $add (@add_list)
+ { Visit_Add ($add, $file_path_copy); }
+
+ # Everything went well.. Print success
+ print " [done] \n";
+ }
+ }
+
+ # At this point all the specializations in all the modules have
+ # succeeded. It is at this point that we need to commit the
+ # specializations in each of the modules. That is move the temporary
+ # file that we created to the main file that was specialized.
+ # This also means that we need another loop and do the same thing
+ # as above....
+ # <module tags>
+ foreach my $module ($transform->getElementsByTagName('module'))
+ {
+ # Complete path name to the module
+ my $module_name = $module->getAttribute('name');
+ my $path_name = $prefix_path . $module_name;
+
+ # Output path name: append output_prefix to the
+ # current module name. Append "/" to create a
+ # directory like /foo/bar/baz/
+ my $output_path = $output_prefix . "/" . $module_name;
+
+ # <file tags>
+ my @files = $module->getElementsByTagName('file');
+
+ # commit the files
+ commit_files ($path_name, $output_path, @files);
+ }
+ }
+}
+
+####
+# Requiured for a module
+####
+1;