Announcement

Collapse
No announcement yet.

Analyzing your PB source code with uCalc Transform

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Analyzing your PB source code with uCalc Transform

    This is another in a series of ways to improve your PowerBASIC source code using uCalc Transform.

    A separate thread, Refactoring your PB source code with uCalc Transform, involves a tool that makes beneficial modifications to your code, whereas this thread involves uCalc patterns for analyzing your code to help pinpoint areas that may need your attention in one way or another.

    Index
    Filtering out Left$, Right$, Mid$, InStr for inspection
    Detecting FOR/NEXT counter variable change within loop
    Listing equates that are declared but unused
    Highlighting equates that are declared but unused
    Finding duplicate equates
    Next
    Last edited by Daniel Corbier; 17 Feb 2014, 09:11 PM. Reason: Updated the index
    Daniel Corbier
    uCalc Fast Math Parser
    uCalc Language Builder
    sigpic

  • #2
    Filtering out Left$, Right$, Mid$, InStr for inspection

    PowerBASIC allows a negative numerical argument for string functions such as Left$, Right$, Mid$, and InStr. While trying to understand how that works, I realized that this functionality wasn't always part of PB. I needed to find out if I had made use of this feature in my own source code. My code has a large number of occurrences of Left$, Right$, Mid$, and InStr, scattered across a number of files. Using a keyword search in the IDE for Left$, one occurrence at a time, and then starting all over with Right$, and then Mid$, then InStr, then doing the whole thing again for each separate #Include file is out of the question, because it would be too tedious, and time-consuming. After clicking the Find Next button in the IDE for the nth time, I'd probably start getting tired, and overlook some occurrences. After many minutes, I'd probably give up altogether on trying to find out.

    How could I rapidly fish those functions (including arguments) out from the rest of my source code, and place them into a neatly sorted list with no duplicates, to make it quick & easy for me to inspect? What if I wanted a list like the one below?

    Code:
    InStr(@t.@ExternalKeywords, ":"+Keyword$)
    InStr(-1, uCalcFileName$, "\")
    InStr(Params$, ",")
    Left$(CodeBlock$, Len(CodeBlock$)-1)
    Left$(TextIn, TextLength)
    Left$(uCalcFileName$, InStr(-1, uCalcFileName$, "\"))
    Mid$(Definition$, TextStart, TextLength)
    Mid$(Expr, i, Len(target$))
    Mid$(Number$, DecimalPoint+1)
    Right$(CurrentLine$, Len(TmpLineContinue$))
    Right$(Definition$, 1)
    Right$(Part$, 1)
    Try this on your own first. Now look at the uCalc Transform solution below:



    The first pattern replaces #include "file.txt" with the contents of the file (and does so recursively for nested #Includes). In a separate pass (Pass: 2), it finds the relevant keywords, along with the args in parentheses. Could it be any simpler?

    When you want to modify a document, you generally click on Transform. Here, instead of modifying the document, the goal is to pluck out relevant pieces from it. So instead of Transform, click on Filter.

    Or, if you just want to highlight and browse all the occurrences right in the document instead of making a separate list, then use the blue navigation buttons.

    Important: In order for the filtered list to be sorted and without duplicates, in the Propreties area (not shown here), I set Sort to True, and Unique to True. Also the Pass once property for the #include pattern is set to False; that way it goes back and passes over the newly inserted #include file text and searches for more pattern matches.

    One thing that might not seem obvious is the use of % in {args%}. What's that all about? At first I just used {arg}. But then I noticed that I had a number of Mid$, InStr, etc statements that were nested, and I wanted each occurrence on a separate line, both the longer occurrence, and the one inside. For instance if I had:

    Code:
    Left$(Mid$(i$, 20), 5)
    I'd want both to be featured in the list:

    Code:
    Left$(Mid$(i$, 20), 5)
    Mid$(i$, 20)
    By using {arg%} with a %, it tells the parser to expand the argument (which in this case allows it to be filtered as a separate entry).

    I wasn't sure if I used negative arguments for InStr, Left$, Right$, or Mid$. But as it turned out, looking at the filtered output made me realize that I actually do make use of a negative value for InStr in some places, but not so with Left$, Right$, or Mid$. This transform was so quick and easy to set up that it was worth the very short amount of time it took to do it. And the result was important enough that I made certain changes in my project because of it.
    Daniel Corbier
    uCalc Fast Math Parser
    uCalc Language Builder
    sigpic

    Comment


    • #3
      Detecting FOR/NEXT counter variable change within loop

      Modifying the counter variable inside the body of a FOR/NEXT loop might introduce subtle bugs in your code. But the PB compiler does not stop you from doing it. Perhaps you are modifying it intentionally in certain places, or perhaps you're doing it without realizing it, and it's causing bugs. Maybe its a mix of the two. Either way, it's useful to spot locations where that's happening. How can you quickly highlight all FOR/NEXT occurrences in your code where the counter is modified in the loop body?

      This simple pattern will do the trick in uCalc Transform:


      Once you type or paste in this pattern (you can leave the Replace with box empty), just click one of the blue navigation buttons. I have circled the navigation buttons in red.

      It will highlight the results for you like this:


      Here's the pattern in a form you can cut-and-paste:
      Code:
      FOR {Counter} = {start} TO {stop} [STEP {increment}] {nl}
         [{statements+}]
         { {nl} | THEN | : } {Counter} =
         [{more_statements+}]
      NEXT
      The pattern should be pretty self explanatory. But here's how you can read it:

      It takes the word (or token) FOR (not case-sensitive) followed by a variable name (represented by {Counter}), followed by the = sign, followed an expression represented by {start}, followed by TO, followed by another expression represented by {stop}, optionally followed by a STEP clause, followed by a new line ({nl}). Let's temporarily skip the explanation for the body; the pattern finishes with the word NEXT. This part is pretty much straight out of the PB help file.

      Instead of:

      Code:
      FOR {Counter} = {start} TO {stop} [STEP {increment}]{nl}
      I could have simply said:

      Code:
      FOR {Counter} = {range}{nl}
      and it would have given the same result, simply capturing everything between = and the new line. It would also work a little faster. But I wanted to make it look more like the help file.

      As for the part between FOR and NEXT, you optionally have some statements. The + in {statements+} means that it could be one ore more statements (a line break is defined as a statement separator in the default Patterns.uc file). The following line:

      Code:
      { {nl} | THEN | : } {Counter} =
      means either a new line ({nl}), or the word THEN, or a : followed by {Counter}. Normally each pattern variable in a pattern must be unique. However by using the same {Counter} between FOR and =, that is used in the above line, this indicates that if the same text is found in those two locations, it's a match.

      Note: If you have lines that have a starting quote but not an ending quote, then this won't work properly. So you may need to refactor your code first for this.
      Daniel Corbier
      uCalc Fast Math Parser
      uCalc Language Builder
      sigpic

      Comment


      • #4
        Listing equates that are declared but unused

        Have you ever wanted to verify equate usage in your source code and see which defined equates were not actually used in the application? Have you put off such a task, because you neither had time to do it by hand nor time/disposition to write a program for it? Or maybe you wrote a program for it, but things like commented equates complicated things?

        With uCalc Transform, there's no need to write a complicated program for this. Just think of how you want to do it, then write just a few simple lines like the ones below which do just that:



        Algorithm: Basically each equate that is defined will be stored in a list. Each equate found in the code that is not an equate definition will be deleted from this list. At the end a list containing all defined equates minus the ones that occur in the code is returned.

        Here's how the implementation works:

        Code:
        {@Var: Equates As SortedList}
        The above line defines a list that will store the equate names.

        Code:
        Find:    ' {Comment:".*"}
        Replace: [Skip over]
        The line above causes it to ignore commented lines.

        Code:
        Find:    %{Equate:1}
        Replace: {@Exec: Delete(Equates , "{Equate}")} 
        
        Find:    {nl}%{Equate:1}
        Replace: {@Exec: Insert(Equates, "{Equate}")}
        The above two patterns must be defined in that order. When you have similar patterns, both of which might match the same text, the one towards the bottom is checked first. If it doesn't match, then it goes up the list. With that in mind, if an equate occurs right after a new line ({nl}), then you know that it is an equate definition. In this case the name of the equate is inserted into the list. If the equate does not start right after a new line, then this is an equate that is actually used in the app. In that case, the name of that equate is deleted from the list.

        Code:
        Find:    {@End}
        Replace: {@Eval: Equates}
        When the end is reached, the list of unused equates is returned. If you click Transform, this list is appended at the end of the code. However, Filter is the button we need here. Everything will be filtered out, except for this line.

        In my own case, I needed to check included .bas files as well, in order to avoid false positives when the equate is actually used in another file. See the first post in this thread for how to handle #Include. You can have it skip over Wine32API, just as we did for comments.

        For a next time:
        • Highlighting and/or removing unused equates
        • Detecting duplicates
        Last edited by Daniel Corbier; 13 Feb 2014, 05:24 PM. Reason: Changed "detecting" to "Listing"
        Daniel Corbier
        uCalc Fast Math Parser
        uCalc Language Builder
        sigpic

        Comment


        • #5
          Highlighting equates that are declared but unused

          As mentioned in the previous post, there are several ways of doing things with uCalc Transform. Here's a different approach both to finding unused equates and to displaying them. Instead of displaying a filtered list of unused equates, I'll instead have it highlight the unused equates right in the original code. This same approach can either highlight the equates, or with a slight modification it can go ahead and remove them. This approach calls for the Transform button instead of the Filter button.

          I will first post a simpler version that only handles numeric equates:

          See equates_highlight.uc

          Unlike the previous approach, there is no list to store equates here. Instead things are broken into 3 passes. In the first pass, when an equate definition (such as %abc = 123) is found, a marker, which I arbitrarily named <hlight> is inserted in front of the equate, in place of %. If the equate occurrence is not an equate definition (such as in Print %abc), then it dynamically defines a search/replace pattern that will return <hlight> back to %, for that particular equate. This is done in Pass: 2. By the end of Pass: 2, the equates that are left with a <hlight> in front of them are those that did not occur elsewhere in the code. So in Pass: 3, <hlight> is changed to %, and at the same time those lines are actually highlighted. Color is selected either by right or left-clicking the color box to the right of the pattern, or by using the properties section).

          After looking at code in the Source Code section of this forum, I realized that I had forgotten about string equates. So I've taken the same approach a step further, and added a parameter (which I've named {type}) that can be either % and $. The final transform looks like this:


          See equates_highlight2.uc

          Removing the unused equates

          If you want to actually remove the unused equates instead of just highlighting them, simply remove {nl}{type}{Equate} from the Replace with box in the last pattern. Although you can leave the box blank, I like to use the special keyword {Nothing}, which is the same as an empty box, but lets me know that the box was not left empty by mistake (which I sometimes do).

          Tips:
          • Regarding the arbitrary name <hlight>, I had also considered things like <highlight> (too long), or <hl> (not descriptive enough). But one bad name I chose which was a cause of frustration for a while was [highlight]. For a moment, I couldn't figure out why it wasn't working. Then I realized that I've given special meaning to square brackets, which represent an optional section. If you ever get stuck, don't despair; just ask me for help with your pattern.
          • I was not able to just type in this pattern from start to finish and get a working solution on first try. I first had to think the approach through even tough the algorithm is straight forward. But I had to experiment as I constructed the transform before I got it to work just right. This meant removing checkmarks temporarily in order to observe intermediate steps.
          Daniel Corbier
          uCalc Fast Math Parser
          uCalc Language Builder
          sigpic

          Comment


          • #6
            Finding duplicate equates

            Today's transform finds duplicate equate definitions. As usual, there are several approaches. I'll simply chose one similar to Post #4. But I'll introduce some other items. Here's what a transform for finding duplicate numeric equates looks like:

            See Equates_dupe.uc

            In Post #4, I defined a sorted list with {@Var: Equates As SortedList}. Now I've used {@Eval: Dim Equates ...}. It does the same. But this way allows me to define several variables on the same line. I defined Dupes as another sorted list.

            I've used a conditional IIF statement, which is familiar to PB users. Index() returns an numeric index value corresponding to the item on the list. If Index() returns a non-0 value, this means we already inserted this equate in the Equates list, so instead we insert it in the Dupes list. At the end I simply return the Dupes list.

            I also introduced the IIF function.
            Daniel Corbier
            uCalc Fast Math Parser
            uCalc Language Builder
            sigpic

            Comment

            Working...
            X