Ask Reuben – November 11, 2025

Abstract Syntax Tree Example

How can I find a complex code pattern in many source files? 

What is an Abstract Syntax Tree?

This is an article I have wanted to write for a while, but I did not have a good real-life example that was short and simple enough to make for a good-sized article.

To provide some background when I first started doing some Genero transformations, over 20 years ago now, in order to find places in the code where changes needed to be made, I would create some shell scripts that used grep etc to find certain code patterns in the code.  Later, once more familiar with Genero I would write a little 4gl program using base.Channel and base.stringBuffer classes to read a source file and look for certain code patterns.  These same techniques could also be used in automated code reviews.  In more recent years I have learnt about the concept of Abstract Syntax Tree (AST), the advantage an AST has is that it is not clouded by comments, whitespace, line breaks,  case (upper vs lower)  etc. that you have to consider when looking for patterns in text.

The Code Quality tool (gslint) that is in Genero Studio utilises an AST (as do other products such as Sonarqube) and if you peek inside some of the gslint sources inside Genero Studio, you will see that it uses an undocumented command line option for fglcomp to generate an AST. The file to find inside Genero Studio is gslintanalyse.4gl, in there look for the function  extractASTFromFile and the line

CALL channel.openPipe(SFMT("fglcomp -Tx %1 \"%2\"", commandLineOptions, path), "r")

If you create a simple little test program …


#! helloworld.4gl
MAIN
   DISPLAY "Hello World"
END MAIN

… and type at the command line …

fglcomp -Tx helloworld.4gl

… you get the following output …


<?xml version='1.0' encoding='UTF-8'?>
<symbol location="helloworld.4gl:1.1-3.8" name="module">
  <symbol location="helloworld.4gl:1.2-1.1" name="compilerOptions"/>
  <symbol location="helloworld.4gl:1.1-1.0" name="optional"/>
  <symbol location="helloworld.4gl:1.2-1.1" name="importL"/>
  <symbol location="helloworld.4gl:1.6-1.5" name="varDefStmtL"/>
  <symbol location="helloworld.4gl:1.1-3.8" name="functionDeclL">
    <symbol location="helloworld.4gl:1.1-3.8" name="main">
      <symbol location="helloworld.4gl:1.6-1.5" name="defineL"/>
      <symbol location="helloworld.4gl:2.4-2.24" name="fglStmtL">
        <symbol location="helloworld.4gl:2.4-2.24" name="display">
          <symbol location="helloworld.4gl:2.12-2.24" name="exprCl">
            <symbol location="helloworld.4gl:2.12-2.24" name="exprCharConst">
              <token location="helloworld.4gl:2.12-2.24" name="CharConst" value="Hello World"/>
            </symbol>
          </symbol>
        </symbol>
      </symbol>
      <symbol location="helloworld.4gl:3.1-3.8" name="endFunction"/>
    </symbol>
  </symbol>
</symbol>

… you’ll note that this XML output with the following properties

  • two types of elements (sometimes called nodes)
    • symbol
    • token
  • three different attributes
    • location attribute which contains the file and line number
    • name attribute
    • value attribute

To find code patterns, is then “simply” a case of finding patterns within these XML nodes. Are there nodes with certain attribute values, and are these ancestors/descendats/siblings of other nodes or are these other nodes missing.   If you dig deeper into the Code Quality gslint code inside Genero Studio, you will see that that is what that code does.

The real world example I had recently that using this AST would help was with code that was not using ui.Interface.refresh method correctly.

When transforming your code from Informix-4gl to Genero, one of the things you need to do if you want to update the display when doing some processing is to use ui.Interface.refresh() to force an update of the display.  This is covered in this page of the documentation as well as this early Ask-Reuben article .  If this is not done correctly, the performance degradation is much more noticeable using Universal Rendering which I covered in a recent WWDC presentation.

So the example of using AST, is to find and highlight these bad code patterns so that they can be corrected.  That would typically mean adding a ui.Interface.refresh call and/or adding an IFMOD … so that it is not output every iteration through the loop.

The bad patterns are a loop that updates the display without a ui.Interface.refresh e.g.

FOR i = 1 TO LOOP_SIZE
    MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
    # processing
END FOR

or a loop that updates the display with a ui.Interface.refresh that occurs every iteration of the loop e.g.

FOR i = 1 TO LOOP_SIZE
    MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
    CALL ui.Interface.refresh()
    # processing 
END FOR

This test would also need to recognise that these code patterns are good.

A loop with no update of the display …

FOR i = 1 TO LOOP_SIZE
    # processing
END FOR

or a loop that has a ui.Interface.refresh that uses MOD to display less often

FOR i = 1 TO LOOP_SIZE
    MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
    IF i MOD 100 = 0 THEN
        CALL ui.Interface.refresh()
    END IF
    # processing
END FOR

So to illustrate this, at the end of the article you will find two files, askreuben303_test.4gl which is the 4gl I am using to test, and askreuben303.4gl which is the program doing the analysis.

If you run askreuben303_test.4gl on its own, when you click Go, you should find that you can see the MESSAGE displaying “Processing record X of 1000” with the X incrementing “quickly”.  I put quickly in quotes because it is not quick, it is slow.  You can see the number incrementing from 1 to 1000 when in reality it should go from 1 to 1000 in the blink of an eye.  The many screen updates result in many transmissions across the network which as well as clogging the network also have to be bundled and unbundled at each end slowing down the GBC.

If you look inside askreuben303_test.4gl, there are 4 FOR loops.  As per the code samples above, the first and last FOR loop in the code are ok, the second and third loops are not ok.  What is needed is a way to identify the code that is ok and the code that is not ok.

What askreuben303.4gl does is it executes fglcomp -Tx on the test 4gl file to produce an Abstract Syntax Tree (AST) and it then analyses it.  You should get the following output …

Processing FOR loop from Filename: askreuben303_test.4gl Line Number: 17
PASS - No message

Processing FOR loop from Filename: askreuben303_test.4gl Line Number: 22
ERROR - Has Message but no ui.Interface.Refresh

Processing FOR loop from Filename: askreuben303_test.4gl Line Number: 28
ERROR - Has Message but no IF MOD before ui.Interface.refresh

Processing FOR loop from Filename: askreuben303_test.4gl Line Number: 35
PASS - Has Message with an IF MOD before ui.Interface.refresh

This tells us that we need to make a correction inside the FOR loops on lines 22 and 28.  The FOR loops on lines 17 and 35 are good.

If you want to see the AST on line 16, there is a commented out DISPLAY doc.saveToString().  If you uncomment that, the AST will be written to stdout.  I have attached that output at the very end of this article.


Before I go deeper into the code, it should be acknowledged that I have simplified this to keep it small enough for this article.

  • Where I am iterating through all the FOR loops,  I should also be iterating through all the FOREACH and WHILE loops as well.
  • Where I am looking for MESSAGE, I should also be looking for DISPLAY TO in case of a form field being updated.
  • I should also consider what happens if a function is called inside the loop, I would have to search in that code as well.
  • I have not considered the value in the MOD expression, I should also have a test to make sure it is a suitable value.

This code is illustrative of the techniques you can use.  For your own requirements, you will have to balance completeness versus time spent when considering how many false positives or negatives you tolerate.

How the code works is that it uses the xml extension package to interrogate the resulting XML.

This code starts the analysis and what it is doing is finding each FOR loop,as represented by a “symbol” element with an attribute “name” that has value “forTo” and then testing that element and its children …

LET for_list = root_node.selectByXPath('.//symbol[@name="forTo"]', NULL)
FOR for_idx = 1 TO for_list.getCount()
    CALL process_for_node(for_list.getitem(for_idx))
END FOR

… from the element representing each FOR loop, it looks to see if they have a descendant that is an element that represents the use of MESSAGE, as represented by a “symbol” element with an attribute “name” that has the value “message” …

LET message_list = for_node.selectByXPath('.//symbol[@name="message"]', NULL)
IF message_list.getCount() = 0 THEN
    DISPLAY "PASS - No message"
RETURN

If there is no MESSAGE, that is good., otherwise we keep testing the FOR loop.

If there is a MESSAGE, we look to see if there is a method call and for every method call we look to see if it is a ui.Interface.refresh as evidenced by the 3 “token” elements that spell out ui.Interface.refresh with the value of their “value” attribute  …

LET token_list = methodCall_node.selectByXPath('.//token', NULL)
IF token_list.getCount() = 3 THEN
    IF token_list.getItem(1).getAttribute("value") = "ui"
    AND token_list.getItem(2).getAttribute("value") = "Interface"
    AND token_list.getItem(3).getAttribute("value") = "refresh" THEN
        RETURN TRUE
    END IF
END IF

Then the processing gets a little more complicated.  There is a little big of recursive logic …

FUNCTION linked_via_if_with_mod(top_node xml.DomNode, test_node xml.DomNode)
   ...
   RETURN linked_via_if_with_mod(top_node, test_node.getParentNode())
END FUNCTION

… as I test each element between a FOR and a ui.Interface elemen by starting at the ui.Interface element and testing its parent until I reach the FOR element.  With the element to test I am looking to see if it is an IF that uses a MOD

IF test_node.getNodeName() = "symbol" AND test_node.getAttribute("name") = "ifThen" THEN
    LET exprOpMo_list = test_node.selectByXPath('.//symbol[@name="exprOpMo"]', NULL)
    IF exprOpMo_list.getCount() > 0 THEN
        RETURN TRUE
    END IF
END IF

The key coding techniques is the use of method calls from the XML package to interrogate the XML document.  These include :-

If you are familiar with these concepts and how to traverse an XML document, this will come quickly.  Otherwise teaching resources such as w3schools   (XML Dom, XPath) might help if you have never encountered these concepts around XML documents before.

Chances are if you do more than one of these you will build up a library of functions you can reuse.  If you look in the gslint sources shipped inside Genero Studio you might find some code you can re-use.


So to wrap up, by generating from your .4gl, an Abstract Syntax Tree which is an XML document, you can use regular XML methods on this document looking for incorrect or missing XML elements and attributes, which represent good or bad coding patterns.  With large volumes of source files, you can use this technique to quickly find places where you must review your source code for probable correction.  This technique is immune to comments in your code, use of the pre-processor, whitespace, case considerations.

I should also reemphasise that fglcomp -Tx is undocumented.  It is however what the Code Quality tool uses, so if it does change one day, our Code Quality tool will have to change as well.


#! askreuben303.4gl
IMPORT xml
MAIN
DEFINE cmd STRING

DEFINE doc xml.DomDocument
DEFINE root_node xml.DomNode
DEFINE for_list xml.DomNodeList
DEFINE for_idx INTEGER

    LET cmd = "fglcomp -Tx askreuben303_test.4gl > askreuben303_test.xml"
    RUN cmd

    LET doc = xml.DomDocument.Create()
    CALL doc.load("askreuben303_test.xml")
    #DISPLAY doc.saveToString()

    LET root_node = doc.getDocumentElement()

    -- Build a list of every FOR loop
    LET for_list = root_node.selectByXPath('.//symbol[@name="forTo"]', NULL)
    FOR for_idx = 1 TO for_list.getCount()
        CALL process_for_node(for_list.getItem(for_idx))
    END FOR
END MAIN



FUNCTION process_for_node(for_node xml.DomNode)
DEFINE message_list xml.DomNodeList

DEFINE methodCall_list  xml.DomNodeList
DEFINE methodCall_node xml.DomNode
DEFINE methodCall_idx INTEGER

    DISPLAY ""
    DISPLAY SFMT("Processing FOR loop from %1", extract_location(for_node))

    -- test if there is a MESSAGE
    LET message_list = for_node.selectByXPath('.//symbol[@name="message"]', NULL)
    IF message_list.getCount() = 0 THEN
        DISPLAY "PASS - No message"
        RETURN
    ELSE
        -- There is a MESSAGE 
        -- iterate through each methodCall
        LET methodCall_list = for_node.selectByXPath('.//symbol[@name="methodCall"]', NULL)
        FOR methodCall_idx = 1 TO methodCall_list.getCount()
            LET methodCall_node = methodCall_list.getItem(methodCall_idx)
            IF methodCall_is_ui_Interface_refresh(methodCall_node) THEN
                -- test if IF with MOD between for_node and methodCall_node
                IF linked_via_if_with_mod(for_node, methodCall_node.getParentNode()) THEN
                    DISPLAY "PASS - Has Message with an IF MOD before ui.Interface.refresh"
                ELSE
                    DISPLAY "ERROR - Has Message but no IF MOD before ui.Interface.refresh"
                END IF
                RETURN 
            END IF
        END FOR
        DISPLAY "ERROR - Has Message but no ui.Interface.Refresh"
        RETURN
    END IF
END FUNCTION



-- Recursively search upwards from test_node, stopping at top_node
-- Return true if there is an ifThen node and a MOD expression
-- What we are looking for
--<symbol location="askreuben303_test.4gl:53.9-56.14" name="ifThen">
--  <symbol location="askreuben303_test.4gl:53.12-53.32" name="exprOpEq">
--    <symbol location="askreuben303_test.4gl:53.12-53.28" name="exprOpMo">
FUNCTION linked_via_if_with_mod(top_node xml.DomNode, test_node xml.DomNode)
DEFINE exprOpMo_list xml.DomNodeList

    IF top_node IS NULL OR test_node IS NULL THEN
        RETURN FALSE
    END IF
    IF top_node = test_node THEN
        RETURN FALSE
    END IF
    IF test_node.getNodeName() = "symbol" AND test_node.getAttribute("name") = "ifThen" THEN
        LET exprOpMo_list = test_node.selectByXPath('.//symbol[@name="exprOpMo"]', NULL)
        IF exprOpMo_list.getCount() > 0 THEN
            RETURN TRUE
        END IF
    END IF
    
    RETURN linked_via_if_with_mod(top_node, test_node.getParentNode())
END FUNCTION




-- RETURN TRUE if methodCall node is for a ui.Interface.refresh() call
-- Test is if 3 token child elements that are ui.Interface.refresh
-- Example format of methodCall with ui.Interface.refresh
--        <symbol location="askreuben303_test.4gl:42.5-42.31" name="methodCall">
--          <symbol location="askreuben303_test.4gl:42.10-42.31" name="refMCall">
--            <symbol location="askreuben303_test.4gl:42.10-42.21" name="refMName">
--              <symbol location="askreuben303_test.4gl:42.10-42.11" name="refName">
--                <token location="askreuben303_test.4gl:42.10-42.11" name="Ident" value="ui"/>
--              </symbol>
--              <token location="askreuben303_test.4gl:42.13-42.21" name="Ident" value="Interface"/>
--            </symbol>
--            <token location="askreuben303_test.4gl:42.23-42.29" name="Ident" value="refresh"/>
--            <symbol location="askreuben303_test.4gl:42.31-42.30" name="optional"/>
--          </symbol>
FUNCTION methodCall_is_ui_Interface_refresh(methodCall_node xml.DomNode)
DEFINE token_list xml.DomNodeList

    LET token_list = methodCall_node.selectByXPath('.//token', NULL)
    IF token_list.getCount() = 3 THEN
        IF token_list.getItem(1).getAttribute("value") = "ui"
        AND token_list.getItem(2).getAttribute("value") = "Interface"
        AND token_list.getItem(3).getAttribute("value") = "refresh" THEN
            RETURN TRUE
        END IF
    END IF
    RETURN FALSE
END FUNCTION



-- Location attribute value is of format location="filename.4gl:start_line.start_character-end_line.end_character"
-- e.g. location="filename.4gl:42.13-42.21"
-- Simplify output to Filename: filename.4gl Line Number: start_line
FUNCTION extract_location(n xml.DomNode)
DEFINE location_value STRING
DEFINE pos1, pos2 INTEGER

    LET location_value = n.getAttribute("location")
    LET pos1 = location_value.getIndexOf(":", 1)
    LET pos2 = location_value.getIndexOf(".", pos1+1)
    RETURN SFMT("Filename: %1 Line Number: %2", location_value.subString(1, pos1-1), location_value.subString(pos1+1, pos2-1))
END FUNCTION

#! askreuben303_test.4gl
MAIN
    MENU ""
        ON ACTION GO
            CALL do_test()
        ON ACTION CLOSE
            EXIT MENU
    END MENU
END MAIN

FUNCTION do_test()
CONSTANT LOOP_SIZE = 1000
CONSTANT REFRESH_MOD = 100
DEFINE i INTEGER

    -- Test of loop without message
    FOR i = 1 TO LOOP_SIZE
        # processing
    END FOR

    -- test of loop with message but no ui.Interface.refresh or mod
    FOR i = 1 TO LOOP_SIZE
        MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
        # processing
    END FOR

    -- test of loop with ui.Interface.refresh but no mod
    FOR i = 1 TO LOOP_SIZE
        MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
        CALL ui.Interface.refresh()
        # processing
    END FOR

    -- test of loop with ui.Interface.refresh inside a mod
    FOR i = 1 TO LOOP_SIZE
        MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
        IF i MOD REFRESH_MOD = 0 THEN
            CALL ui.Interface.refresh()
        END IF
    END FOR

    MESSAGE ""
    CALL ui.Interface.refresh()
END FUNCTION

#! Output of fglcomp -Tx askreuben303_test.4gl

<?xml version="1.0" encoding="UTF-8"?><symbol location="askreuben303_test.4gl:1.1-44.12" name="module">
  <symbol location="askreuben303_test.4gl:2.2-2.1" name="compilerOptions"/>
  <symbol location="askreuben303_test.4gl:2.1-2.0" name="optional"/>
  <symbol location="askreuben303_test.4gl:2.2-2.1" name="importL"/>
  <symbol location="askreuben303_test.4gl:2.6-2.5" name="varDefStmtL"/>
  <symbol location="askreuben303_test.4gl:2.1-44.12" name="functionDeclL">
    <symbol location="askreuben303_test.4gl:2.1-9.8" name="main">
      <symbol location="askreuben303_test.4gl:2.6-2.5" name="defineL"/>
      <symbol location="askreuben303_test.4gl:3.5-8.12" name="fglStmtL">
        <symbol location="askreuben303_test.4gl:3.5-8.12" name="menu">
          <symbol location="askreuben303_test.4gl:3.10-3.11" name="stringCharConst">
            <token location="askreuben303_test.4gl:3.10-3.11" name="CharConst" value=""/>
          </symbol>
          <symbol location="askreuben303_test.4gl:4.9-4.8" name="optional"/>
          <symbol location="askreuben303_test.4gl:4.9-7.21" name="menuCommandL">
            <symbol location="askreuben303_test.4gl:4.9-5.26" name="menuCommand">
              <symbol location="askreuben303_test.4gl:4.9-4.20" name="onAction">
                <token location="askreuben303_test.4gl:4.19-4.20" name="Ident" value="GO"/>
              </symbol>
              <symbol location="askreuben303_test.4gl:5.13-5.26" name="fglStmtL">
                <symbol location="askreuben303_test.4gl:5.13-5.26" name="methodCall">
                  <symbol location="askreuben303_test.4gl:5.18-5.26" name="refFunction">
                    <token location="askreuben303_test.4gl:5.18-5.24" name="Ident" value="do_test"/>
                    <symbol location="askreuben303_test.4gl:5.26-5.25" name="optional"/>
                  </symbol>
                  <symbol location="askreuben303_test.4gl:6.9-6.8" name="optional"/>
                </symbol>
              </symbol>
            </symbol>
            <symbol location="askreuben303_test.4gl:6.9-7.21" name="menuCommand">
              <symbol location="askreuben303_test.4gl:6.9-6.23" name="onAction">
                <token location="askreuben303_test.4gl:6.19-6.23" name="Ident" value="CLOSE"/>
              </symbol>
              <symbol location="askreuben303_test.4gl:7.13-7.21" name="fglStmtL">
                <symbol location="askreuben303_test.4gl:7.13-7.21" name="exitMenu"/>
              </symbol>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:8.5-8.12" name="endMenu"/>
        </symbol>
      </symbol>
      <symbol location="askreuben303_test.4gl:9.1-9.8" name="endFunction"/>
    </symbol>
    <symbol location="askreuben303_test.4gl:11.1-44.12" name="function">
      <symbol location="askreuben303_test.4gl:11.1-11.0" name="optional"/>
      <token location="askreuben303_test.4gl:11.10-11.16" name="Ident" value="do_test"/>
      <symbol location="askreuben303_test.4gl:11.18-11.18" name="optional"/>
      <symbol location="askreuben303_test.4gl:12.1-12.0" name="optional"/>
      <symbol location="askreuben303_test.4gl:12.1-12.0" name="optional"/>
      <symbol location="askreuben303_test.4gl:12.1-14.16" name="defineL">
        <symbol location="askreuben303_test.4gl:12.1-12.25" name="constant">
          <symbol location="askreuben303_test.4gl:12.10-12.25" name="constantDefCl">
            <symbol location="askreuben303_test.4gl:12.10-12.25" name="constantDef">
              <token location="askreuben303_test.4gl:12.10-12.18" name="Ident" value="LOOP_SIZE"/>
              <symbol location="askreuben303_test.4gl:12.20-12.19" name="optional"/>
              <symbol location="askreuben303_test.4gl:12.22-12.25" name="exprIntConst">
                <token location="askreuben303_test.4gl:12.22-12.25" name="IntegerConst" value="1000"/>
              </symbol>
            </symbol>
          </symbol>
        </symbol>
        <symbol location="askreuben303_test.4gl:13.1-13.26" name="constant">
          <symbol location="askreuben303_test.4gl:13.10-13.26" name="constantDefCl">
            <symbol location="askreuben303_test.4gl:13.10-13.26" name="constantDef">
              <token location="askreuben303_test.4gl:13.10-13.20" name="Ident" value="REFRESH_MOD"/>
              <symbol location="askreuben303_test.4gl:13.22-13.21" name="optional"/>
              <symbol location="askreuben303_test.4gl:13.24-13.26" name="exprIntConst">
                <token location="askreuben303_test.4gl:13.24-13.26" name="IntegerConst" value="100"/>
              </symbol>
            </symbol>
          </symbol>
        </symbol>
        <symbol location="askreuben303_test.4gl:14.1-14.16" name="define">
          <symbol location="askreuben303_test.4gl:14.8-14.16" name="varDefCl">
            <symbol location="askreuben303_test.4gl:14.8-14.16" name="varDef">
              <symbol location="askreuben303_test.4gl:14.8-14.8" name="identCl">
                <token location="askreuben303_test.4gl:14.8-14.8" name="Ident" value="i"/>
              </symbol>
              <symbol location="askreuben303_test.4gl:14.10-14.16" name="typeInt">
                <symbol location="askreuben303_test.4gl:17.5-17.4" name="optional"/>
              </symbol>
            </symbol>
          </symbol>
        </symbol>
      </symbol>
      <symbol location="askreuben303_test.4gl:17.5-43.31" name="fglStmtL">
        <symbol location="askreuben303_test.4gl:17.5-19.11" name="forTo">
          <symbol location="askreuben303_test.4gl:17.9-17.9" name="varName">
            <symbol location="askreuben303_test.4gl:17.9-17.9" name="refName">
              <token location="askreuben303_test.4gl:17.9-17.9" name="Ident" value="i"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:17.13-17.13" name="exprIntConst">
            <token location="askreuben303_test.4gl:17.13-17.13" name="IntegerConst" value="1"/>
          </symbol>
          <symbol location="askreuben303_test.4gl:17.18-17.26" name="exprVal">
            <symbol location="askreuben303_test.4gl:17.18-17.26" name="refName">
              <token location="askreuben303_test.4gl:17.18-17.26" name="Ident" value="LOOP_SIZE"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:19.5-19.4" name="fglStmtL"/>
          <symbol location="askreuben303_test.4gl:19.5-19.11" name="endFor"/>
        </symbol>
        <symbol location="askreuben303_test.4gl:22.5-25.11" name="forTo">
          <symbol location="askreuben303_test.4gl:22.9-22.9" name="varName">
            <symbol location="askreuben303_test.4gl:22.9-22.9" name="refName">
              <token location="askreuben303_test.4gl:22.9-22.9" name="Ident" value="i"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:22.13-22.13" name="exprIntConst">
            <token location="askreuben303_test.4gl:22.13-22.13" name="IntegerConst" value="1"/>
          </symbol>
          <symbol location="askreuben303_test.4gl:22.18-22.26" name="exprVal">
            <symbol location="askreuben303_test.4gl:22.18-22.26" name="refName">
              <token location="askreuben303_test.4gl:22.18-22.26" name="Ident" value="LOOP_SIZE"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:23.9-23.64" name="fglStmtL">
            <symbol location="askreuben303_test.4gl:23.9-23.64" name="message">
              <symbol location="askreuben303_test.4gl:23.17-23.64" name="exprCl">
                <symbol location="askreuben303_test.4gl:23.17-23.64" name="expr4glFunction">
                  <symbol location="askreuben303_test.4gl:23.17-23.64" name="stringFormat">
                    <symbol location="askreuben303_test.4gl:23.22-23.49" name="exprCharConst">
                      <token location="askreuben303_test.4gl:23.22-23.49" name="CharConst" value="Processing record %1 of %2"/>
                    </symbol>
                    <symbol location="askreuben303_test.4gl:23.52-23.63" name="exprCl">
                      <symbol location="askreuben303_test.4gl:23.52-23.52" name="exprVal">
                        <symbol location="askreuben303_test.4gl:23.52-23.52" name="refName">
                          <token location="askreuben303_test.4gl:23.52-23.52" name="Ident" value="i"/>
                        </symbol>
                      </symbol>
                      <symbol location="askreuben303_test.4gl:23.55-23.63" name="exprVal">
                        <symbol location="askreuben303_test.4gl:23.55-23.63" name="refName">
                          <token location="askreuben303_test.4gl:23.55-23.63" name="Ident" value="LOOP_SIZE"/>
                        </symbol>
                      </symbol>
                    </symbol>
                  </symbol>
                </symbol>
              </symbol>
              <symbol location="askreuben303_test.4gl:25.5-25.4" name="optional"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:25.5-25.11" name="endFor"/>
        </symbol>
        <symbol location="askreuben303_test.4gl:28.5-32.11" name="forTo">
          <symbol location="askreuben303_test.4gl:28.9-28.9" name="varName">
            <symbol location="askreuben303_test.4gl:28.9-28.9" name="refName">
              <token location="askreuben303_test.4gl:28.9-28.9" name="Ident" value="i"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:28.13-28.13" name="exprIntConst">
            <token location="askreuben303_test.4gl:28.13-28.13" name="IntegerConst" value="1"/>
          </symbol>
          <symbol location="askreuben303_test.4gl:28.18-28.26" name="exprVal">
            <symbol location="askreuben303_test.4gl:28.18-28.26" name="refName">
              <token location="askreuben303_test.4gl:28.18-28.26" name="Ident" value="LOOP_SIZE"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:29.9-30.35" name="fglStmtL">
            <symbol location="askreuben303_test.4gl:29.9-29.64" name="message">
              <symbol location="askreuben303_test.4gl:29.17-29.64" name="exprCl">
                <symbol location="askreuben303_test.4gl:29.17-29.64" name="expr4glFunction">
                  <symbol location="askreuben303_test.4gl:29.17-29.64" name="stringFormat">
                    <symbol location="askreuben303_test.4gl:29.22-29.49" name="exprCharConst">
                      <token location="askreuben303_test.4gl:29.22-29.49" name="CharConst" value="Processing record %1 of %2"/>
                    </symbol>
                    <symbol location="askreuben303_test.4gl:29.52-29.63" name="exprCl">
                      <symbol location="askreuben303_test.4gl:29.52-29.52" name="exprVal">
                        <symbol location="askreuben303_test.4gl:29.52-29.52" name="refName">
                          <token location="askreuben303_test.4gl:29.52-29.52" name="Ident" value="i"/>
                        </symbol>
                      </symbol>
                      <symbol location="askreuben303_test.4gl:29.55-29.63" name="exprVal">
                        <symbol location="askreuben303_test.4gl:29.55-29.63" name="refName">
                          <token location="askreuben303_test.4gl:29.55-29.63" name="Ident" value="LOOP_SIZE"/>
                        </symbol>
                      </symbol>
                    </symbol>
                  </symbol>
                </symbol>
              </symbol>
              <symbol location="askreuben303_test.4gl:30.9-30.8" name="optional"/>
            </symbol>
            <symbol location="askreuben303_test.4gl:30.9-30.35" name="methodCall">
              <symbol location="askreuben303_test.4gl:30.14-30.35" name="refMCall">
                <symbol location="askreuben303_test.4gl:30.14-30.25" name="refMName">
                  <symbol location="askreuben303_test.4gl:30.14-30.15" name="refName">
                    <token location="askreuben303_test.4gl:30.14-30.15" name="Ident" value="ui"/>
                  </symbol>
                  <token location="askreuben303_test.4gl:30.17-30.25" name="Ident" value="Interface"/>
                </symbol>
                <token location="askreuben303_test.4gl:30.27-30.33" name="Ident" value="refresh"/>
                <symbol location="askreuben303_test.4gl:30.35-30.34" name="optional"/>
              </symbol>
              <symbol location="askreuben303_test.4gl:32.5-32.4" name="optional"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:32.5-32.11" name="endFor"/>
        </symbol>
        <symbol location="askreuben303_test.4gl:35.5-40.11" name="forTo">
          <symbol location="askreuben303_test.4gl:35.9-35.9" name="varName">
            <symbol location="askreuben303_test.4gl:35.9-35.9" name="refName">
              <token location="askreuben303_test.4gl:35.9-35.9" name="Ident" value="i"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:35.13-35.13" name="exprIntConst">
            <token location="askreuben303_test.4gl:35.13-35.13" name="IntegerConst" value="1"/>
          </symbol>
          <symbol location="askreuben303_test.4gl:35.18-35.26" name="exprVal">
            <symbol location="askreuben303_test.4gl:35.18-35.26" name="refName">
              <token location="askreuben303_test.4gl:35.18-35.26" name="Ident" value="LOOP_SIZE"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:36.9-39.14" name="fglStmtL">
            <symbol location="askreuben303_test.4gl:36.9-36.64" name="message">
              <symbol location="askreuben303_test.4gl:36.17-36.64" name="exprCl">
                <symbol location="askreuben303_test.4gl:36.17-36.64" name="expr4glFunction">
                  <symbol location="askreuben303_test.4gl:36.17-36.64" name="stringFormat">
                    <symbol location="askreuben303_test.4gl:36.22-36.49" name="exprCharConst">
                      <token location="askreuben303_test.4gl:36.22-36.49" name="CharConst" value="Processing record %1 of %2"/>
                    </symbol>
                    <symbol location="askreuben303_test.4gl:36.52-36.63" name="exprCl">
                      <symbol location="askreuben303_test.4gl:36.52-36.52" name="exprVal">
                        <symbol location="askreuben303_test.4gl:36.52-36.52" name="refName">
                          <token location="askreuben303_test.4gl:36.52-36.52" name="Ident" value="i"/>
                        </symbol>
                      </symbol>
                      <symbol location="askreuben303_test.4gl:36.55-36.63" name="exprVal">
                        <symbol location="askreuben303_test.4gl:36.55-36.63" name="refName">
                          <token location="askreuben303_test.4gl:36.55-36.63" name="Ident" value="LOOP_SIZE"/>
                        </symbol>
                      </symbol>
                    </symbol>
                  </symbol>
                </symbol>
              </symbol>
              <symbol location="askreuben303_test.4gl:37.9-37.8" name="optional"/>
            </symbol>
            <symbol location="askreuben303_test.4gl:37.9-39.14" name="ifThen">
              <symbol location="askreuben303_test.4gl:37.12-37.32" name="exprOpEq">
                <symbol location="askreuben303_test.4gl:37.12-37.28" name="exprOpMo">
                  <symbol location="askreuben303_test.4gl:37.12-37.12" name="exprVal">
                    <symbol location="askreuben303_test.4gl:37.12-37.12" name="refName">
                      <token location="askreuben303_test.4gl:37.12-37.12" name="Ident" value="i"/>
                    </symbol>
                  </symbol>
                  <symbol location="askreuben303_test.4gl:37.18-37.28" name="exprVal">
                    <symbol location="askreuben303_test.4gl:37.18-37.28" name="refName">
                      <token location="askreuben303_test.4gl:37.18-37.28" name="Ident" value="REFRESH_MOD"/>
                    </symbol>
                  </symbol>
                </symbol>
                <symbol location="askreuben303_test.4gl:37.32-37.32" name="exprIntConst">
                  <token location="askreuben303_test.4gl:37.32-37.32" name="IntegerConst" value="0"/>
                </symbol>
              </symbol>
              <symbol location="askreuben303_test.4gl:38.13-38.39" name="fglStmtL">
                <symbol location="askreuben303_test.4gl:38.13-38.39" name="methodCall">
                  <symbol location="askreuben303_test.4gl:38.18-38.39" name="refMCall">
                    <symbol location="askreuben303_test.4gl:38.18-38.29" name="refMName">
                      <symbol location="askreuben303_test.4gl:38.18-38.19" name="refName">
                        <token location="askreuben303_test.4gl:38.18-38.19" name="Ident" value="ui"/>
                      </symbol>
                      <token location="askreuben303_test.4gl:38.21-38.29" name="Ident" value="Interface"/>
                    </symbol>
                    <token location="askreuben303_test.4gl:38.31-38.37" name="Ident" value="refresh"/>
                    <symbol location="askreuben303_test.4gl:38.39-38.38" name="optional"/>
                  </symbol>
                  <symbol location="askreuben303_test.4gl:39.9-39.8" name="optional"/>
                </symbol>
              </symbol>
              <symbol location="askreuben303_test.4gl:39.9-39.8" name="optional"/>
              <symbol location="askreuben303_test.4gl:39.9-39.14" name="endIf"/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:40.5-40.11" name="endFor"/>
        </symbol>
        <symbol location="askreuben303_test.4gl:42.5-42.14" name="message">
          <symbol location="askreuben303_test.4gl:42.13-42.14" name="exprCl">
            <symbol location="askreuben303_test.4gl:42.13-42.14" name="exprCharConst">
              <token location="askreuben303_test.4gl:42.13-42.14" name="CharConst" value=""/>
            </symbol>
          </symbol>
          <symbol location="askreuben303_test.4gl:43.5-43.4" name="optional"/>
        </symbol>
        <symbol location="askreuben303_test.4gl:43.5-43.31" name="methodCall">
          <symbol location="askreuben303_test.4gl:43.10-43.31" name="refMCall">
            <symbol location="askreuben303_test.4gl:43.10-43.21" name="refMName">
              <symbol location="askreuben303_test.4gl:43.10-43.11" name="refName">
                <token location="askreuben303_test.4gl:43.10-43.11" name="Ident" value="ui"/>
              </symbol>
              <token location="askreuben303_test.4gl:43.13-43.21" name="Ident" value="Interface"/>
            </symbol>
            <token location="askreuben303_test.4gl:43.23-43.29" name="Ident" value="refresh"/>
            <symbol location="askreuben303_test.4gl:43.31-43.30" name="optional"/>
          </symbol>
          <symbol location="askreuben303_test.4gl:44.1-44.0" name="optional"/>
        </symbol>
      </symbol>
      <symbol location="askreuben303_test.4gl:44.1-44.12" name="endFunction"/>
    </symbol>
  </symbol>
</symbol>