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 If you create a simple little test program … … and type at the command line … … you get the following output … … you’ll note that this XML output with the following properties 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 IF … MOD … 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. or a loop that updates the display with a ui.Interface.refresh that occurs every iteration of the loop e.g. This test would also need to recognise that these code patterns are good. A loop with no update of the display … or a loop that has a ui.Interface.refresh that uses MOD to display less often 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 … 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. 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 … … 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” … 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 … Then the processing gets a little more complicated. There is a little big of recursive logic … … 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 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.CALL channel.openPipe(SFMT("fglcomp -Tx %1 \"%2\"", commandLineOptions, path), "r")
#! helloworld.4gl
MAIN
DISPLAY "Hello World"
END MAIN
fglcomp -Tx helloworld.4gl
<?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>
FOR i = 1 TO LOOP_SIZE
MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
# processing
END FOR
FOR i = 1 TO LOOP_SIZE
MESSAGE SFMT("Processing record %1 of %2", i, LOOP_SIZE)
CALL ui.Interface.refresh()
# processing
END FOR
FOR i = 1 TO LOOP_SIZE
# processing
END FOR
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
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
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
LET message_list = for_node.selectByXPath('.//symbol[@name="message"]', NULL)
IF message_list.getCount() = 0 THEN
DISPLAY "PASS - No message"
RETURN
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
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
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
#! 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>

