• You've discovered RedGuides, an EverQuest multi-boxing and scripting community 🧙‍♀️⚙️. We want you to play several EQ characters at once, come join us and say hello! 👋

  • A TLP without truebox has thawed (Very Vanilla ready)
    Frostreaver

.MAC and .INC simple formatter

joojoobee

A Member to Remember
Creator
Joined
May 15, 2016
RedCents
4,327¢
V.9 -- Update to only save old versions of files that have been modified. Previous versions saved everything, which was creating hundreds of never-used files.
V.7 -- Minor edit to ensure tabs and spaces are considered the same for ' } else ' formatting.

This is a PERL formatter for .MAC and .INC files to "beautify" messy code. I wrote it to be able to better read my own code, and read other people's code (which can be a mix of spaces and tab indents, which can play havoc in different editors). This is especially useful for visual clarity with complex logic chains of /if && /for statements. Plus, I am OCD about code styling.

Basically, the program indents code according to standard rules for { } brackets and /for /next statements. The code also creates a header for subroutines if one doesn't exist. The program creates a subfolder called Processed into which it places your old .MAC /.INC scripts (so original code is not lost). MQ2f.pl never overwrites anything in the Processed folder, it just increments the file number if a duplication-- so nothing is ever lost.

The MQ2f.pl program will:
  • Read all .MAC and .INC folders in the folder in which the MQ2f.pl file is placed.
  • Increments indents on { and /for statements.
  • Decrements indents in } and } /else and /next statements.
  • Temporarily decrements (for one line) all comments "|" and gotos ":" -- this is for visual clarity.
  • Will use either tabs (default) or spaces for indents (use s switch on CMD line for spaces).
  • Will create a subroutine "header" for subroutines if no header exists. Inserts 1 blank line if it's a newly created header, respects spacing if already a header.
  • Will remove empty lines if 2 or more in a row.
  • Moves the "old" files to a subfolder called Processed (respecting overwrites with a numbered file approach file.mac.0, file.mac.1 etc.).
  • Writes the newly formatted file (same name) back into the folder in which the MQ2f.pl was run.
To run this:
  • Place the MQ2f.pl file in any folder in which you will place .MAC and .INC files.
  • Open a CMD line.
  • Type "perl mq2f.pl" or "perl mq2f.pl s" (don't use the quotes, silly). Hit return.
  • the t switch will use tabs rather than tabs for the indents.
I ran this on the entire Macros folder from the release (all .MAC and .INC files) and it took about 3 seconds to complete.

Requires PERL. PERL Install instructions are at the bottom of this post after the MQ2f.pl script.

MQ2f.pl V9-- Defaults to leading tabs. Use s switch for leading tabs. Last Updated: 6/23/2019
Rich (BB code):
# Version 9     6/22/2019  by Joojoobee
#!/usr/bin/perl -w
#use strict;
use File::Spec::Functions qw'catfile';
use File::Copy qw'move';
use autodie    qw'move';
use File::Stat qw/:stat/;
use File::Compare;


   my $proc_file = "";
   my $countindents = 1;      # default to at least one indent for whole file
   my $countEmptyRows = 0;    # counter to ensure only one empty row allowed.  If 2 or more, remove those lines
   my $subCommentCount = 0;   # check for a subroutine header
   my $oldSubCommentCount = 0;
   my $subroutineTag = 0;     # Was there a subroutine tag?
   my $subroutineName = "";   # name of the subroutine to place in a header
   my $useTabsOrSpaces = "\t";
   my $TabOrSpaceUse = " using tabs for indents";
   my $modtime8 = time();
   my $modtime9 = time();
   my $newmac= "";
   my $oldfile = "";

   my $msg = '';

   if (exists $ARGV[0] && $ARGV[0] eq "s") { $useTabsOrSpaces = "  "; $TabOrSpaceUse = " using spaces for indents"; }

   foreach $proc_file (<*.*>)
   {
       if (!($proc_file=~ m/.mac$|.inc$/)) { goto nextFile; }  # only read .mac or .int files
       #print "$proc_file\t\t ... Opened  \n";
       $oldfile = $proc_file;
       open(FILE, "$proc_file");
       $modtime8 = (stat($proc_file))[8];
       $modtime9 = (stat($proc_file))[9];

       $countindents = 0;                     # reset the indent count for each new file
       if ($proc_file=~ m/.mac/) {            # if it's a .mac file, rename output file to .fmac and open it for writing
         $proc_file =~ s/.mac//;              # get the base name of the file
         open (OUT22,">$proc_file".".fmac");  # create a temporary version for writing
         $newmac = "$proc_file".".fmac";
       }
       if ($proc_file=~ m/.inc/) {            # if it's a .inc file, rename output file to .finc and open it for writing
         $proc_file =~ s/.inc//;              # get the base name of the file
         open (OUT22,">$proc_file".".finc");  # create a temporary version for writing
         $newmac = "$proc_file".".finc";
       }

       while(my $row = <FILE> )               # get each line in file
       {
           my $addback = 0;                   # set | and : check to 0
           chomp $row;                        # clean up the last row, remove line feed/CR tags
           $row =~ s/^\s+|\s+$//g;            # remove leading and trailing tabs and spaces
           my $string="";                     # reset the indent string to blank for each line

           if ($countindents <0) {      $countindents = 0; }                # in case of weird over use of }
           if (($row=~ m/}$/) && index($row,"{") < 0) { $countindents--; }  # decrement the indent for end of bracket }
           if ($row=~ m/}\s*else/i) {       $countindents--; }                # decrement the indent for end of bracket } else (case insensitive)
           if ($row=~ m/^\/next\s*\S/i) {     $countindents--; }            # decrement the indent for a next statment
           if ($row=~ m/^\|/ || $row=~ m/^:/) {                             # decrement the indent for comments or : goto lines
               #$countindents--;
               #$addback = 1;                                               # set the addback flag to put indent back after | or :
           }

           if (($row=~ m/^\|\s*---/ || $row=~ m/^\|\s*=-=/ || $row=~ m/^\||\s*---/ || $row=~ m/^\||\s*=-=/) && ($subCommentCount == 0 || $subCommentCount == 2)) { $subCommentCount++; }  # start a simple count for subroutine header
           if (($row=~ m/^\|\s*sub/i || $row=~ m/^\|\s*===/  || $row=~ m/^\|:\s*sub/i || $row=~ m/^\|:\s*===/)&& $subCommentCount == 1)   { $subCommentCount++; }                         # found a reasonable tag for "| SUB".  Case insensitive and tolerant of variation
           if ($row=~ m/^sub\s\S/i && ($subCommentCount == 3 || $subCommentCount == 0)) {                                                                                                 # found a subroutine tag (looks for word "sub" a space/tab and at least a single non space/tab letter)
                  $subroutineTag++;
                  $subroutineName = $row;
                  $subroutineName =~ s/^sub\s//i;     # remove the sub keyword
                  $subroutineName =~ s/\(.*\)//i;     # remove any variable names and ()
           }

           if ($oldSubCommentCount !=0 && $oldSubCommentCount == $subCommentCount) {  # Check for Header should not allow for any interceding lines.
            $countLines++;
            if ($countLines == 2) {           # two lines went by without a Header key, this is not a header
                  $subCommentCount = 0;           # reset header check to 0
                  $oldSubCommentCount = 0;
                  $countLines = 0;
            }
           }

           $oldSubCommentCount = $subCommentCount;    # save subCommentCount for comparison to determine if proper structure.

           if ($row ne "") {                          # build the indents
            for my $i (0 .. $countindents) {
              $string = "$string"."$useTabsOrSpaces";   # by concatenating tabs or spaces
            }
           }

           if ($row eq "") {        # check for empty lines
            $countEmptyRows++;
           }

           if ($countEmptyRows == 2) {      # two empty lines in a row, skip printing it
            $countEmptyRows = 1;
            next;
           }

           if ($subroutineTag == 1) {     # a sub keyword was found
                if ($subCommentCount != 3) {    # if the lines immediately above did not already have a header, print a header
                    $subCommentCount = 0;       # reset the header check counters, print a header
                    $oldSubCommentCount = 0;
                    $countLines = 0;
                    if ($countEmptyRows > 0) { print OUT22 "\n";  }

                    print OUT22 "|: --------------------------------------------------------------------------------------------\n";
                    print OUT22 "|: SUB: $subroutineName\n";
                    print OUT22 "|: --------------------------------------------------------------------------------------------\n";
                }
                if ($subroutineTag == 1 && $subCommentCount > 0) {   #incomplete header check, so zero the header check
                    $subCommentCount = 0;
                    $oldSubCommentCount = 0;
                    $countLines = 0;
                }

              $subroutineTag = 0;       # finished with header, reset subroutine line check
            }

           if ($subCommentCount > 3) {        # invalid header structure, so zero the header check
            $subCommentCount = 0;
            $oldSubCommentCount = 0;
            $countLines = 0;
           }

           if ($row =~ m/\+\+\+  Formatted by MQ2f/i) { next; }  # dom't rewrite the end line.

           if ($countEmptyRows <= 1) {                  # allow printing if as many as 1 empty line above
               print OUT22 "$string"."$row\n";          # print the line
               if ($row ne "") {$countEmptyRows = 0;}   # was a single empty line prior, reset counter of empty lines to 0
           }

           if ($addback == 1)     { $countindents++; }        # add back the line if de-indented for | or :
           if($row=~ m/{$/)       { $countindents++; }        # increment the indent if an open bracket { at the end of the line
           if($row=~ m/^\/for\s+\S/i )  { $countindents++; }  # increment the indent if a /for statement
           if($row=~ m/^\/squelch\s*\/for\s*\S/i )  { $countindents++; }  # increment the indent if a /squelch /for statement
        }

       close FILE;  # close input file

       if ($countEmptyRows > 0) { print OUT22 "\n";  }
       print OUT22 "\n[+++  Formatted by MQ2f"."$TabOrSpaceUse +++]\n" ;
       close OUT22; # close output file

       utime $modtime8, $modtime9, $newmac;
      
       my $directory = "Processed";         # If Processed subdirectory does not exist, create one
       unless(-e $directory or mkdir $directory) {
         die "Unable to create $directory, potential permission or space limitations\n";
       }
     
        if (compare($oldfile,$newmac) != 0) {
            print "reformatted ==> $oldfile\n";
           
            my $origin      = '.';
            my $destination = 'Processed';
            my $path = catfile $destination, $oldfile;
            my $counter = 0;

              while( -e $path ){
                $counter++;
                $path = catfile $destination, "$oldfile.$counter";
              }
             
              move( catfile( $origin, $oldfile ), $path );
             
              my $modtime8 = (stat($newmac))[8];
              my $modtime9 = (stat($newmac))[9];

              my $newname = $newmac;
              if ($oldfile=~ m/.mac/) {
                $newname =~ s{.fmac}{.mac}gxms;
              }
              if ($oldfile=~ m/.inc/) {
                $newname =~ s{.finc}{.inc}gxms;
              }
             
              rename $newmac, $newname;
              utime $modtime8, $modtime9, $newmac;
       
        } else {
          unlink $newmac;
        } 
       
       nextFile:
   }

   print "Exiting \n";
   my $exit = 0;

Installing Perl:
I find PERL to be amazingly flexible at complex string editing. It has great abilities to "reach" into the Windows system as well. It's not as easy as AHK, but is great for a lot of quick and dirty programming.

General instructions for installing PERL are here: https://www.perl.org/get.html

I use ActiveState PERL (Strawberry Perl and others also work). There is a community version (free of charge).
http://www.activestate.com/activeperl/downloads
Install:
  • Download PERLinstall package (32 and 64 bit available)
  • Run exe file
  • Activestate loads all necessary files, including the Perl Package Manager (PPM).
  • PPM keeps track of module updates, allows you to search for and load unique modules for math and string functions.
  • All modules used in MQ2f.pl are contained in the default PERL download.

Running MQ2f.pl :
  • Open a cmd window in the folder in which MQ2f.pl resides along with the .MAC or .INC files you want to convert.
  • At command line, type "perl MQ2f.pl" (no quotes) or with options for tabs:
    • perl MQ2f.pl t
Editors and IDE for PERL: https://perlmaven.com/perl-editor

There are lots of online tutorials for Perl script newbies. Most often you will find yourself at PerlMaven as he has excellent examples and tutorials (he also wrote the free IDE so he knows what he's doing).
 
Last edited:
I never thought i would see perl scripts for MQ2 here at RG. Very cool. thank you for sharing.
 
Lama-- Sure.

Before:
Rich (BB code):
| Will extract sub header and organize this messy subroutine
  SUB Test2(int newbie)
  /if (((${Me.XTarget}>2)||(${Spawn[id ${Me.XTarget[1].ID}].Named})) && !${Me.Casting.ID} && ${debuffcasttime}==0) {
  |---First Line of Defence
    					/if (${Me.AltAbilityReady[1472]}&&${Spell[${Me.AltAbility[1472]}].Stacks}) {
      /docommand /alt act 1472
      															/varset debuffcasttime 8
    				} else /if (${Me.AltAbilityReady[8500]}&&${Spell[${Me.AltAbility[8500]}].Stacks}) {
      							/docommand /alt act 8500
      														/varset debuffcasttime 8
    } else /if (!${Me.Buff[Healing Frenzy].ID} && ${Me.AltAbilityReady[997]}&&${Spell[${Me.AltAbility[997]}].Stacks}) {
/docommand /alt act 997
      							/varset debuffcasttime 8
    } else /if (!${Me.Buff[Third Spire of Divinity].ID} && ${Me.AltAbilityReady[38]}&&${Spell[${Me.AltAbility[38]}].Stacks}) {
    												  /docommand /alt act 38
     												 /varset debuffcasttime 8
    |---Second Ability to go with First Line
 												   } else /if (!${Me.Buff[Channeling the Divine].ID} && ${Me.AltAbilityReady[515]}&&${Spell[${Me.AltAbility[515]}].Stacks}) {
  						    /docommand /alt act 515
  											    /varset debuffcasttime 8
    } else /if (!${Me.Buff[Improved Twincast].ID} && ${Me.AltAbilityReady[706]}&&${Spell[${Me.AltAbility[706]}].Stacks}) {
    									  /docommand /alt act 706
   												   /varset debuffcasttime 8
   																						 } else /if (${Me.AltAbilityReady[6488]}&&${Spell[${Me.AltAbility[6488]}].Stacks}) {
      										/docommand /alt act 6488
      /varset debuffcasttime 8
   															 }
  }
  													/return



|  Example-- will not touch a pre-existing sub header
|----------------------------------------------------------------------------
| SUB: ExtraHeal4 for Named spawns or multiple targets
|----------------------------------------------------------------------------
  Sub ExtraHeal4
  
  
  
  
  
  
  |  But will extract a subroutine name and create a header if it doesn't exist.
  Sub Example(int string)

After (using "space" option rather than default tab for indent):
Rich (BB code):
| Will extract sub header and organize this messy subroutine
| --------------------------------------------------------------------------------------------
| SUB: Test2
| --------------------------------------------------------------------------------------------
  SUB Test2(int newbie)
  /if (((${Me.XTarget}>2)||(${Spawn[id ${Me.XTarget[1].ID}].Named})) && !${Me.Casting.ID} && ${debuffcasttime}==0) {
  |---First Line of Defence
    /if (${Me.AltAbilityReady[1472]}&&${Spell[${Me.AltAbility[1472]}].Stacks}) {
      /docommand /alt act 1472
      /varset debuffcasttime 8
    } else /if (${Me.AltAbilityReady[8500]}&&${Spell[${Me.AltAbility[8500]}].Stacks}) {
      /docommand /alt act 8500
      /varset debuffcasttime 8
    } else /if (!${Me.Buff[Healing Frenzy].ID} && ${Me.AltAbilityReady[997]}&&${Spell[${Me.AltAbility[997]}].Stacks}) {
      /docommand /alt act 997
      /varset debuffcasttime 8
    } else /if (!${Me.Buff[Third Spire of Divinity].ID} && ${Me.AltAbilityReady[38]}&&${Spell[${Me.AltAbility[38]}].Stacks}) {
      /docommand /alt act 38
      /varset debuffcasttime 8
    |---Second Ability to go with First Line
    } else /if (!${Me.Buff[Channeling the Divine].ID} && ${Me.AltAbilityReady[515]}&&${Spell[${Me.AltAbility[515]}].Stacks}) {
      /docommand /alt act 515
      /varset debuffcasttime 8
    } else /if (!${Me.Buff[Improved Twincast].ID} && ${Me.AltAbilityReady[706]}&&${Spell[${Me.AltAbility[706]}].Stacks}) {
      /docommand /alt act 706
      /varset debuffcasttime 8
    } else /if (${Me.AltAbilityReady[6488]}&&${Spell[${Me.AltAbility[6488]}].Stacks}) {
      /docommand /alt act 6488
      /varset debuffcasttime 8
    }
  }
  /return

|  Example-- will not touch a pre-existing sub header
|----------------------------------------------------------------------------
| SUB: ExtraHeal4 for Named spawns or multiple targets
|----------------------------------------------------------------------------
  Sub ExtraHeal4

|  But will extract a subroutine name and create a header if it doesn't exist.
| --------------------------------------------------------------------------------------------
| SUB: Example
| --------------------------------------------------------------------------------------------
  Sub Example(int string)
 
Too much organization kills my creativity... just kidding, that shit is awesome!
 
Thanks Raz and Maskoi...

It took about 5 seconds to convert the entire Macros folder in the release. If anyone doesn't want to install PERL just for this, you can copy the code here and I will convert it and repost, or PM me your email so we can exchange info and you can email me the script, and I will convert/return.

I've found organization to be useful for tracking/editing deeply embedded /if structures and (perhaps) overly complicated logic chains.

Raz-- If you want, I can create another script that scrambles your code for you for maximizing your creativity! :hfive:
 
oh I scramble my own code JUST fine! lmao.... although I should send you eoknuke, would love to see it all pretty and shiny for like 10 minutes until my brain says, oh look a squirrel and I add 50 more lines of code to do something I want them to do...
 
This looks awesome. I don't do a ton of macro writing, but this will definitely come in handy when I do.
 
Last edited:
Works as advertised. I'm not familiar with perl. I would like it to not indent the Sub Subname after the sub header. Afraid that I don't really see where to keep it from doing that.
I feel like the check would go with
Rich (BB code):
 if ($countindents <0) {              $countindents = 0; }    # in case of weird over use of }        if (($row=~ m/}$/) && index($row,"{") < 0) { $countindents--; }    # decrement the indent for end of bracket }
        if ($row=~ m/} else/i) {              $countindents--; }    # decrement the indent for end of bracket } else (case insensitive)
        if ($row=~ m/^\/next\s*\S/i) {          $countindents--; }    # decrement the indent for a next statment
        if ($row=~ m/^\|\|/ ) { goto beyondComments; }
        if ($row=~ m/^\|/ || $row=~ m/^:/) {                # decrement the indent for comments or : goto lines
           $countindents--;
           $addback = 1;                            # set the addback flag to put indent back after | or :
        }
the above checks. But I'm not really sure I understand exactly what those are saying per-say. if the row = jibberish ;-x decrement the indent lol.

I feel like
Rich (BB code):
if ($row=~ m/Sub/i) {              $countindents--; }
should work. Anyone familiar with Perl that can let me know if I'm on the right track?

~~~Update~~~
It seems I was correct. Adding that above
Rich (BB code):
if ($row=~ m/^\|\|/ ) { goto beyondComments; }
removed the indent from the Sub lines and now this formats exactly as I wanted.

Below it the modified version that doesn't indent Sub declaration line.

Rich (BB code):
#!/usr/bin/perl -w#use strict;
use File::Spec::Functions qw'catfile';
use File::Copy qw'move';
use autodie    qw'move';


   my $proc_file = "";
   my $countindents = 1;        # default to at least one indent for whole file
   my $countEmptyRows = 0;        # counter to ensure only one empty row allowed.  If 2 or more, remove those lines
   my $subCommentCount = 0;        # check for a subroutine header
   my $oldSubCommentCount = 0;
   my $subroutineTag = 0;        # Was there a subroutine tag?
   my $subroutineName = "";        # name of the subroutine to place in a header
   my $useTabsOrSpaces = "  ";
   my $TabOrSpaceUse = " using spaces for indents";
   
   if (exists $ARGV[0] && $ARGV[0] eq "t") { $useTabsOrSpaces = "\t"; $TabOrSpaceUse = " using tabs for indents"; }
   
   foreach $proc_file (<*.*>)
   {      
       if (!($proc_file=~ m/.mac$|.inc$/)) { goto nextFile; }  # only read .mac or .int files
       
       print "$proc_file\t\t ... Opened  \n";
       open(FILE, "$proc_file");     
       
       $countindents = 0;                # reset the indent count for each new file
       if ($proc_file=~ m/.mac/) {            # if it's a .mac file, rename output file to .fmac and open it for writing
           $proc_file =~ s/.mac//;              # get the base name of the file          
           open (OUT22,">$proc_file".".fmac");    # create a temporary version for writing
     }
       if ($proc_file=~ m/.inc/) {            # if it's a .inc file, rename output file to .finc and open it for writing
           $proc_file =~ s/.inc//;                # get the base name of the file    
           open (OUT22,">$proc_file".".finc");    # create a temporary version for writing
     }
       
       while(my $row = <FILE> )                # get each line in file
       {
        my $addback = 0;                # set | and : check to 0
        chomp $row;                    # clean up the last row, remove line feed/CR tags
        $row =~ s/^\s+|\s+$//g;                    # remove leading and trailing tabs and spaces
        my $string="";                # reset the indent string to blank for each line
        
        if ($countindents <0) {              $countindents = 0; }    # in case of weird over use of }
        if (($row=~ m/}$/) && index($row,"{") < 0) { $countindents--; }    # decrement the indent for end of bracket }
        if ($row=~ m/} else/i) {              $countindents--; }    # decrement the indent for end of bracket } else (case insensitive)
        if ($row=~ m/^\/next\s*\S/i) {          $countindents--; }    # decrement the indent for a next statment
        if ($row=~ m/Sub /i) {              $countindents--; }
        if ($row=~ m/^\|\|/ ) { goto beyondComments; }
        if ($row=~ m/^\|/ || $row=~ m/^:/) {                # decrement the indent for comments or : goto lines
           $countindents--;
           $addback = 1;                            # set the addback flag to put indent back after | or :
        }
        beyondComments:
        if (($row=~ m/^\|\s*---/ || $row=~ m/^\|\s*=-=/) && ($subCommentCount == 0 || $subCommentCount == 2)) { $subCommentCount++; }    # start a simple count for subroutine header
        if (($row=~ m/^\|\s*sub/i || $row=~ m/^\|\s*===/)&& $subCommentCount == 1)     { $subCommentCount++; }                                                     # found a reasonable tag for "| SUB".  Case insensitive and tolerant of variation
        if ($row=~ m/^sub\s\S/i && ($subCommentCount == 3 || $subCommentCount == 0)) {                                                                                                 # found a subroutine tag (looks for word "sub" a space/tab and at least a single non space/tab letter)
        $subroutineTag++;
        $subroutineName = $row;
        $subroutineName =~ s/^sub\s//i;            # remove the sub keyword
        $subroutineName =~ s/\(.*\)//i;          # remove any variable names and ()  
        }
        
        if ($oldSubCommentCount !=0 && $oldSubCommentCount == $subCommentCount) {    # Check for Header should not allow for any interceding lines.
        $countLines++;
        if ($countLines == 2) {                                # two lines went by without a Header key, this is not a header
            $subCommentCount = 0;                            # reset header check to 0
            $oldSubCommentCount = 0;
            $countLines = 0;
        }
        }
        
        $oldSubCommentCount = $subCommentCount;        # save subCommentCount for comparison to determine if proper structure.
          
        if ($row ne "") {                        # build the indents
        for my $i (0 .. $countindents) {        
              $string = "$string"."$useTabsOrSpaces";     # by concatenating tabs or spaces
        }
        }
        
        if ($row eq "") {                    # check for empty lines
        $countEmptyRows++;
        }
        if ($countEmptyRows == 2) {                    # two empty lines in a row, skip printing it
        $countEmptyRows = 1;
        next;
        }
        
        if ($subroutineTag == 1) {            # a sub keyword was found
        if ($subCommentCount != 3) {    # if the lines immediately above did not already have a header, print a header
            $subCommentCount = 0;        # reset the header check counters, print a header
            $oldSubCommentCount = 0;
            $countLines = 0;
            if ($countEmptyRows > 0) { print OUT22 "\n";  }
            
            print OUT22 "| --------------------------------------------------------------------------------------------\n";
            print OUT22 "| SUB: $subroutineName\n";
            print OUT22 "| --------------------------------------------------------------------------------------------\n";
        }
        if ($subroutineTag == 1 && $subCommentCount > 0) {   #incomplete header check, so zero the header check
            $subCommentCount = 0;
            $oldSubCommentCount = 0;
            $countLines = 0;
        }
        $subroutineTag = 0;                # finished with header, reset subroutine line check
        }
         
        if ($subCommentCount > 3) {                # invalid header structure, so zero the header check
        $subCommentCount = 0;
        $oldSubCommentCount = 0;
        $countLines = 0;
        }
        
        if ($row =~ m/\+\+\+]/i) { next; }                  # remove junk.
        if ($row =~ m/\+\+\+  Formatted by MQ2f /i) { next; }      # don't rewrite the end line.
        
        if ($countEmptyRows <= 1) {                       # allow printing if as many as 1 empty line above
           print OUT22 "$string"."$row\n";                # print the line
           if ($row ne "") {$countEmptyRows = 0;}            # was a single empty line prior, reset counter of empty lines to 0
        }   
        
        if ($addback == 1)                 { $countindents++; }    # add back the line if de-indented for | or : 
        if ($row!~ m/^\|/ && $row=~ m/{$/)  { $countindents++; }    # increment the indent if an open bracket { at the end of the line
        if ($row=~ m/^\/for\s*\S/i )        { $countindents++; }    # increment the indent if a /for statement
        }
       
       close FILE;    # close input file
 
       if ($countEmptyRows > 0) { print OUT22 "\n";  }
       print OUT22 "|  [+++  Formatted by MQ2f"."$TabOrSpaceUse +++]\n" ;
       close OUT22;    # close output file
       print "$proc_file    \t\t ... Closed\n\n";
       
       nextFile:
   }
   
   my $directory = "Processed";                    # If Processed subdirectory does not exist, create one
     unless(-e $directory or mkdir $directory) {
         die "Unable to create $directory, potential permission or space limitations\n";        
     }
   
   # Move all .MAC files to Processed subdirectory.  If filename already exists, create an incremented version.
   foreach $proc_file (<*.mac>)    {
    my $filename    = $proc_file;
    my $origin      = '.';
    my $destination = 'Processed';
    
    my $path = catfile $destination, $filename;
    {
      my $counter;
      while( -e $path ){
        $counter++;
        $path = catfile $destination, "$filename.$counter";
      }
    }
    move( catfile( $origin, $filename ), $path );   
   }  
   
   # rename all the .fmac files to .mac
   foreach $proc_file (<*.fmac>)
   {
    my $newname = $proc_file;
    $newname =~ s{.fmac}{.mac}gxms;
    rename $proc_file, $newname;
   }
   
   # Move all .INC files to Processed subdirectory.  If filename already exists, create an incremented version.
   foreach $proc_file (<*.inc>)    {
    my $filename    = $proc_file;
    my $origin      = '.';
    my $destination = 'Processed';
    
    my $path = catfile $destination, $filename;
    {
      my $counter;
      while( -e $path ){
        $counter++;
        $path = catfile $destination, "$filename.$counter";
      }
    }
    move( catfile( $origin, $filename ), $path );   
   }
   # rename all the .finc files to .inc
   foreach $proc_file (<*.finc>)
   {
    my $newname = $proc_file;
    $newname =~ s{.finc}{.inc}gxms;
    rename $proc_file, $newname;
   }
   
   print "Exiting \n";
   my $exit = 0;
 
So I actually forgot to decrement the /return line and after a little research I also found an issue with my addition that would decrement lines where the word Sub was present in a non-sub declaration such as

Rich (BB code):
	/varset CurrentSub Announce
	/call Debug 3 "${CurrentSub} entered DevCommonPremium.inc"

In the above case both lines were being decremented. I've solved that using m/^\ searchstring/i
where ^\ would search for it at the start of a line only.

Subsequently I've changed this to only use tabs because I don't like spaces, which prevents me from having to run it from a command line or creating a shortcut to add the switch. I can now just double click MQ2F.pl to run it in the folder it's currently in.

Thus the completed product is below.

Rich (BB code):
#!/usr/bin/perl -w#use strict;
use File::Spec::Functions qw'catfile';
use File::Copy qw'move';
use autodie    qw'move';


   my $proc_file = "";
   my $countindents = 1;        # default to at least one indent for whole file
   my $countEmptyRows = 0;        # counter to ensure only one empty row allowed.  If 2 or more, remove those lines
   my $subCommentCount = 0;        # check for a subroutine header
   my $oldSubCommentCount = 0;
   my $subroutineTag = 0;        # Was there a subroutine tag?
   my $subroutineName = "";        # name of the subroutine to place in a header
   my $useTabsOrSpaces = "\t";
   my $TabOrSpaceUse = " using tabs for indents";
   
   foreach $proc_file (<*.*>)
   {      
       if (!($proc_file=~ m/.mac$|.inc$/)) { goto nextFile; }  # only read .mac or .int files
       
       print "$proc_file\t\t ... Opened  \n";
       open(FILE, "$proc_file");     
       
       $countindents = 0;                # reset the indent count for each new file
       if ($proc_file=~ m/.mac/) {            # if it's a .mac file, rename output file to .fmac and open it for writing
           $proc_file =~ s/.mac//;              # get the base name of the file          
           open (OUT22,">$proc_file".".fmac");    # create a temporary version for writing
     }
       if ($proc_file=~ m/.inc/) {            # if it's a .inc file, rename output file to .finc and open it for writing
           $proc_file =~ s/.inc//;                # get the base name of the file    
           open (OUT22,">$proc_file".".finc");    # create a temporary version for writing
     }
       
       while(my $row = <FILE> )                # get each line in file
       {
        my $addback = 0;                # set | and : check to 0
        chomp $row;                    # clean up the last row, remove line feed/CR tags
        $row =~ s/^\s+|\s+$//g;                    # remove leading and trailing tabs and spaces
        my $string="";                # reset the indent string to blank for each line
        
        if ($countindents <0) {              $countindents = 0; }    # in case of weird over use of }
        if (($row=~ m/}$/) && index($row,"{") < 0) { $countindents--; }    # decrement the indent for end of bracket }
        if ($row=~ m/} else/i) {              $countindents--; }    # decrement the indent for end of bracket } else (case insensitive)
        if ($row=~ m/^\/next\s*\S/i) {          $countindents--; }    # decrement the indent for a next statment
		if ($row=~ m/^\Sub /i) {              $countindents--; }
		if ($row=~ m/^\/return/i) {          $countindents--; }
        if ($row=~ m/^\|\|/ ) { goto beyondComments; }
        if ($row=~ m/^\|/ || $row=~ m/^:/) {                # decrement the indent for comments or : goto lines
           $countindents--;
           $addback = 1;                            # set the addback flag to put indent back after | or :
        }
        beyondComments:
        if (($row=~ m/^\|\s*---/ || $row=~ m/^\|\s*=-=/) && ($subCommentCount == 0 || $subCommentCount == 2)) { $subCommentCount++; }    # start a simple count for subroutine header
        if (($row=~ m/^\|\s*sub/i || $row=~ m/^\|\s*===/)&& $subCommentCount == 1)     { $subCommentCount++; }                                                     # found a reasonable tag for "| SUB".  Case insensitive and tolerant of variation
        if ($row=~ m/^sub\s\S/i && ($subCommentCount == 3 || $subCommentCount == 0)) {                                                                                                 # found a subroutine tag (looks for word "sub" a space/tab and at least a single non space/tab letter)
        $subroutineTag++;
        $subroutineName = $row;
        $subroutineName =~ s/^sub\s//i;            # remove the sub keyword
        $subroutineName =~ s/\(.*\)//i;          # remove any variable names and ()  
        }
        
        if ($oldSubCommentCount !=0 && $oldSubCommentCount == $subCommentCount) {    # Check for Header should not allow for any interceding lines.
        $countLines++;
        if ($countLines == 2) {                                # two lines went by without a Header key, this is not a header
            $subCommentCount = 0;                            # reset header check to 0
            $oldSubCommentCount = 0;
            $countLines = 0;
        }
        }
        
        $oldSubCommentCount = $subCommentCount;        # save subCommentCount for comparison to determine if proper structure.
          
        if ($row ne "") {                        # build the indents
        for my $i (0 .. $countindents) {        
              $string = "$string"."$useTabsOrSpaces";     # by concatenating tabs or spaces
        }
        }
        
        if ($row eq "") {                    # check for empty lines
        $countEmptyRows++;
        }
        if ($countEmptyRows == 2) {                    # two empty lines in a row, skip printing it
        $countEmptyRows = 1;
        next;
        }
        
        if ($subroutineTag == 1) {            # a sub keyword was found
        if ($subCommentCount != 3) {    # if the lines immediately above did not already have a header, print a header
            $subCommentCount = 0;        # reset the header check counters, print a header
            $oldSubCommentCount = 0;
            $countLines = 0;
            if ($countEmptyRows > 0) { print OUT22 "\n";  }
            
            print OUT22 "| --------------------------------------------------------------------------------------------\n";
            print OUT22 "| SUB: $subroutineName\n";
            print OUT22 "| --------------------------------------------------------------------------------------------\n";
        }
        if ($subroutineTag == 1 && $subCommentCount > 0) {   #incomplete header check, so zero the header check
            $subCommentCount = 0;
            $oldSubCommentCount = 0;
            $countLines = 0;
        }
        $subroutineTag = 0;                # finished with header, reset subroutine line check
        }
         
        if ($subCommentCount > 3) {                # invalid header structure, so zero the header check
        $subCommentCount = 0;
        $oldSubCommentCount = 0;
        $countLines = 0;
        }
        
        if ($row =~ m/\+\+\+]/i) { next; }                  # remove junk.
        if ($row =~ m/\+\+\+  Formatted by MQ2f /i) { next; }      # don't rewrite the end line.
        
        if ($countEmptyRows <= 1) {                       # allow printing if as many as 1 empty line above
           print OUT22 "$string"."$row\n";                # print the line
           if ($row ne "") {$countEmptyRows = 0;}            # was a single empty line prior, reset counter of empty lines to 0
        }   
        
        if ($addback == 1)                 { $countindents++; }    # add back the line if de-indented for | or : 
        if ($row!~ m/^\|/ && $row=~ m/{$/)  { $countindents++; }    # increment the indent if an open bracket { at the end of the line
        if ($row=~ m/^\/for\s*\S/i )        { $countindents++; }    # increment the indent if a /for statement
        }
       
       close FILE;    # close input file
 
       if ($countEmptyRows > 0) { print OUT22 "\n";  }
       print OUT22 "|  [+++  Formatted by MQ2f"."$TabOrSpaceUse +++]\n" ;
       close OUT22;    # close output file
       print "$proc_file    \t\t ... Closed\n\n";
       
       nextFile:
   }
   
   my $directory = "Processed";                    # If Processed subdirectory does not exist, create one
     unless(-e $directory or mkdir $directory) {
         die "Unable to create $directory, potential permission or space limitations\n";        
     }
   
   # Move all .MAC files to Processed subdirectory.  If filename already exists, create an incremented version.
   foreach $proc_file (<*.mac>)    {
    my $filename    = $proc_file;
    my $origin      = '.';
    my $destination = 'Processed';
    
    my $path = catfile $destination, $filename;
    {
      my $counter;
      while( -e $path ){
        $counter++;
        $path = catfile $destination, "$filename.$counter";
      }
    }
    move( catfile( $origin, $filename ), $path );   
   }  
   
   # rename all the .fmac files to .mac
   foreach $proc_file (<*.fmac>)
   {
    my $newname = $proc_file;
    $newname =~ s{.fmac}{.mac}gxms;
    rename $proc_file, $newname;
   }
   
   # Move all .INC files to Processed subdirectory.  If filename already exists, create an incremented version.
   foreach $proc_file (<*.inc>)    {
    my $filename    = $proc_file;
    my $origin      = '.';
    my $destination = 'Processed';
    
    my $path = catfile $destination, $filename;
    {
      my $counter;
      while( -e $path ){
        $counter++;
        $path = catfile $destination, "$filename.$counter";
      }
    }
    move( catfile( $origin, $filename ), $path );   
   }
   # rename all the .finc files to .inc
   foreach $proc_file (<*.finc>)
   {
    my $newname = $proc_file;
    $newname =~ s{.finc}{.inc}gxms;
    rename $proc_file, $newname;
   }
   
   print "Exiting \n";
   my $exit = 0;
 
This is a PERL formatter for .MAC and .INC files to "beautify" messy code. I wrote it to be able to better read my own code, and read other people's code (which can be a mix of spaces and tab indents, which can play havoc in different editors). This is especially useful for visual clarity with complex logic chains of /if && /for statements. Plus, I am OCD about code styling.

Basically, the program indents code according to standard rules for { } brackets and /for /next statements. The code also creates a header for subroutines if one doesn't exist. The program creates a subfolder called Processed into which it places your old .MAC /.INC scripts (so original code is not lost). MQ2f.pl never overwrites anything in the Processed folder, it just increments the file number if a duplication-- so nothing is ever lost.

The MQ2f.pl program will:
  • Read all .MAC and .INC folders in the folder in which the MQ2f.pl file is placed.
  • Increments indents on { and /for statements.
  • Decrements indents in } and } /else and /next statements.
  • Temporarily decrements (for one line) all comments "|" and gotos ":" -- this is for visual clarity.
  • Will use either tabs (default) or spaces for indents (use s switch on CMD line for spaces).
  • Will create a subroutine "header" for subroutines if no header exists. Inserts 1 blank line if it's a newly created header, respects spacing if already a header.
  • Will remove empty lines if 2 or more in a row.
  • Moves the "old" files to a subfolder called Processed (respecting overwrites with a numbered file approach file.mac.0, file.mac.1 etc.).
  • Writes the newly formatted file (same name) back into the folder in which the MQ2f.pl was run.
To run this:
  • Place the MQ2f.pl file in any folder in which you will place .MAC and .INC files.
  • Open a CMD line.
  • Type "perl mq2f.pl" or "perl mq2f.pl s" (don't use the quotes, silly). Hit return.
  • the t switch will use tabs rather than tabs for the indents.
I ran this on the entire Macros folder from the release (all .MAC and .INC files) and it took about 3 seconds to complete.

Requires PERL. PERL Install instructions are at the bottom of this post after the MQ2f.pl script.

MQ2f.pl V6-- Defaults to leading spaces. Use t switch for leading tabs. Last Updated: 1/29/2017
Rich (BB code):
#!/usr/bin/perl -w
#use strict;
use File::Spec::Functions qw'catfile';
use File::Copy qw'move';
use autodie    qw'move';

   my $proc_file = "";
   my $countindents = 1;        # default to at least one indent for whole file
   my $countEmptyRows = 0;        # counter to ensure only one empty row allowed.  If 2 or more, remove those lines
   my $subCommentCount = 0;        # check for a subroutine header
   my $oldSubCommentCount = 0;
   my $subroutineTag = 0;        # Was there a subroutine tag?
   my $subroutineName = "";        # name of the subroutine to place in a header
   my $useTabsOrSpaces = "  ";
   my $TabOrSpaceUse = " using spaces for indents";
   
   if (exists $ARGV[0] && $ARGV[0] eq "t") { $useTabsOrSpaces = "\t"; $TabOrSpaceUse = " using tabs for indents"; }
   
   foreach $proc_file (<*.*>)
   {      
       if (!($proc_file=~ m/.mac$|.inc$/)) { goto nextFile; }  # only read .mac or .int files
       
       print "$proc_file\t\t ... Opened  \n";
       open(FILE, "$proc_file");     
       
       $countindents = 0;                # reset the indent count for each new file
       if ($proc_file=~ m/.mac/) {            # if it's a .mac file, rename output file to .fmac and open it for writing
           $proc_file =~ s/.mac//;              # get the base name of the file          
           open (OUT22,">$proc_file".".fmac");    # create a temporary version for writing
     }
       if ($proc_file=~ m/.inc/) {            # if it's a .inc file, rename output file to .finc and open it for writing
           $proc_file =~ s/.inc//;                # get the base name of the file    
           open (OUT22,">$proc_file".".finc");    # create a temporary version for writing
     }
       
       while(my $row = <FILE> )                # get each line in file
       {
        my $addback = 0;                # set | and : check to 0
        chomp $row;                    # clean up the last row, remove line feed/CR tags
        $row =~ s/^\s+|\s+$//g;                    # remove leading and trailing tabs and spaces
        my $string="";                # reset the indent string to blank for each line
        
        if ($countindents <0) {              $countindents = 0; }    # in case of weird over use of }
        if (($row=~ m/}$/) && index($row,"{") < 0) { $countindents--; }    # decrement the indent for end of bracket }
        if ($row=~ m/} else/i) {              $countindents--; }    # decrement the indent for end of bracket } else (case insensitive)
        if ($row=~ m/^\/next\s*\S/i) {          $countindents--; }    # decrement the indent for a next statment
        if ($row=~ m/^\|\|/ ) { goto beyondComments; }
        if ($row=~ m/^\|/ || $row=~ m/^:/) {                # decrement the indent for comments or : goto lines
           $countindents--;
           $addback = 1;                            # set the addback flag to put indent back after | or :
        }
        beyondComments:
        if (($row=~ m/^\|\s*---/ || $row=~ m/^\|\s*=-=/) && ($subCommentCount == 0 || $subCommentCount == 2)) { $subCommentCount++; }    # start a simple count for subroutine header
        if (($row=~ m/^\|\s*sub/i || $row=~ m/^\|\s*===/)&& $subCommentCount == 1)     { $subCommentCount++; }                                                     # found a reasonable tag for "| SUB".  Case insensitive and tolerant of variation
        if ($row=~ m/^sub\s\S/i && ($subCommentCount == 3 || $subCommentCount == 0)) {                                                                                                 # found a subroutine tag (looks for word "sub" a space/tab and at least a single non space/tab letter)
        $subroutineTag++;
        $subroutineName = $row;
        $subroutineName =~ s/^sub\s//i;            # remove the sub keyword
        $subroutineName =~ s/\(.*\)//i;          # remove any variable names and ()  
        }
        
        if ($oldSubCommentCount !=0 && $oldSubCommentCount == $subCommentCount) {    # Check for Header should not allow for any interceding lines.
        $countLines++;
        if ($countLines == 2) {                                # two lines went by without a Header key, this is not a header
            $subCommentCount = 0;                            # reset header check to 0
            $oldSubCommentCount = 0;
            $countLines = 0;
        }
        }
        
        $oldSubCommentCount = $subCommentCount;        # save subCommentCount for comparison to determine if proper structure.
          
        if ($row ne "") {                        # build the indents
        for my $i (0 .. $countindents) {        
              $string = "$string"."$useTabsOrSpaces";     # by concatenating tabs or spaces
        }
        }
        
        if ($row eq "") {                    # check for empty lines
        $countEmptyRows++;
        }
        if ($countEmptyRows == 2) {                    # two empty lines in a row, skip printing it
        $countEmptyRows = 1;
        next;
        }
        
        if ($subroutineTag == 1) {            # a sub keyword was found
        if ($subCommentCount != 3) {    # if the lines immediately above did not already have a header, print a header
            $subCommentCount = 0;        # reset the header check counters, print a header
            $oldSubCommentCount = 0;
            $countLines = 0;
            if ($countEmptyRows > 0) { print OUT22 "\n";  }
            
            print OUT22 "| --------------------------------------------------------------------------------------------\n";
            print OUT22 "| SUB: $subroutineName\n";
            print OUT22 "| --------------------------------------------------------------------------------------------\n";
        }
        if ($subroutineTag == 1 && $subCommentCount > 0) {   #incomplete header check, so zero the header check
            $subCommentCount = 0;
            $oldSubCommentCount = 0;
            $countLines = 0;
        }
        $subroutineTag = 0;                # finished with header, reset subroutine line check
        }
         
        if ($subCommentCount > 3) {                # invalid header structure, so zero the header check
        $subCommentCount = 0;
        $oldSubCommentCount = 0;
        $countLines = 0;
        }
        
        if ($row =~ m/\+\+\+]/i) { next; }                  # remove junk.
        if ($row =~ m/\+\+\+  Formatted by MQ2f /i) { next; }      # don't rewrite the end line.
        
        if ($countEmptyRows <= 1) {                       # allow printing if as many as 1 empty line above
           print OUT22 "$string"."$row\n";                # print the line
           if ($row ne "") {$countEmptyRows = 0;}            # was a single empty line prior, reset counter of empty lines to 0
        }   
        
        if ($addback == 1)                 { $countindents++; }    # add back the line if de-indented for | or : 
        if ($row!~ m/^\|/ && $row=~ m/{$/)  { $countindents++; }    # increment the indent if an open bracket { at the end of the line
        if ($row=~ m/^\/for\s*\S/i )        { $countindents++; }    # increment the indent if a /for statement
        }
       
       close FILE;    # close input file
 
       if ($countEmptyRows > 0) { print OUT22 "\n";  }
       print OUT22 "|  [+++  Formatted by MQ2f"."$TabOrSpaceUse +++]\n" ;
       close OUT22;    # close output file
       print "$proc_file    \t\t ... Closed\n\n";
       
       nextFile:
   }
   
   my $directory = "Processed";                    # If Processed subdirectory does not exist, create one
     unless(-e $directory or mkdir $directory) {
         die "Unable to create $directory, potential permission or space limitations\n";        
     }
   
   # Move all .MAC files to Processed subdirectory.  If filename already exists, create an incremented version.
   foreach $proc_file (<*.mac>)    {
    my $filename    = $proc_file;
    my $origin      = '.';
    my $destination = 'Processed';
    
    my $path = catfile $destination, $filename;
    {
      my $counter;
      while( -e $path ){
        $counter++;
        $path = catfile $destination, "$filename.$counter";
      }
    }
    move( catfile( $origin, $filename ), $path );   
   }  
   
   # rename all the .fmac files to .mac
   foreach $proc_file (<*.fmac>)
   {
    my $newname = $proc_file;
    $newname =~ s{.fmac}{.mac}gxms;
    rename $proc_file, $newname;
   }
   
   # Move all .INC files to Processed subdirectory.  If filename already exists, create an incremented version.
   foreach $proc_file (<*.inc>)    {
    my $filename    = $proc_file;
    my $origin      = '.';
    my $destination = 'Processed';
    
    my $path = catfile $destination, $filename;
    {
      my $counter;
      while( -e $path ){
        $counter++;
        $path = catfile $destination, "$filename.$counter";
      }
    }
    move( catfile( $origin, $filename ), $path );   
   }
   # rename all the .finc files to .inc
   foreach $proc_file (<*.finc>)
   {
    my $newname = $proc_file;
    $newname =~ s{.finc}{.inc}gxms;
    rename $proc_file, $newname;
   }
   
   print "Exiting \n";
   my $exit = 0;

Installing Perl:
I find PERL to be amazingly flexible at complex string editing. It has great abilities to "reach" into the Windows system as well. It's not as easy as AHK, but is great for a lot of quick and dirty programming.

General instructions for installing PERL are here: https://www.perl.org/get.html

I use ActiveState PERL (Strawberry Perl and others also work). There is a community version (free of charge).
http://www.activestate.com/activeperl/downloads
Install:
  • Download PERLinstall package (32 and 64 bit available)
  • Run exe file
  • Activestate loads all necessary files, including the Perl Package Manager (PPM).
  • PPM keeps track of module updates, allows you to search for and load unique modules for math and string functions.
  • All modules used in MQ2f.pl are contained in the default PERL download.

Running MQ2f.pl :
  • Open a cmd window in the folder in which MQ2f.pl resides along with the .MAC or .INC files you want to convert.
  • At command line, type "perl MQ2f.pl" (no quotes) or with options for tabs:
    • perl MQ2f.pl t
Editors and IDE for PERL: https://perlmaven.com/perl-editor

There are lots of online tutorials for Perl script newbies. Most often you will find yourself at PerlMaven as he has excellent examples and tutorials (he also wrote the free IDE so he knows what he's doing).
No real coder uses spaces ... Tab is the best way. [emoji3]

Sent from my LG-H871 using Tapatalk
 
Minor update... keeps the file date accessed and modified to be the same as it was. In other words, the formatting does not change the file attribute-- at least not the time modified.

JJB
 
Version 9. Update to only save those files that have been modified. Per the last update, the script keeps the original dates on all saved file.
 
Last edited:
Who has time for spaces? Just setup the IDE so a tab is actually 2 spaces!
 
.MAC and .INC simple formatter

Users who are viewing this thread

Back
Top
Cart