|
The purpose of this article is to provide enough background information about Proparse so that we are able to talk about more interesting things, namely, applications of the parser like Prolint. Proparse is a DLL (or shared library on Unix). There is a 4GL interface to Proparse which makes it easy to use from the 4GL. Here's the basic three lines necessary for using Proparse:
DEFINE VARIABLE parserHandle AS HANDLE NO-UNDO.
RUN proparse/api/proparse.p PERSISTENT SET parserHandle.
{proparse/api/proparse.i parserHandle}
Once we've run proparse.p persistent, we're pretty much done with parserHandle. We now refer to all of Proparse's API via functions which are declared in proparse.i. Those function names all begin with "parser". Now to parse a program file:
IF parserParse("hello.p") = FALSE THEN DO:
/* error handling here */
END.
Note that in order for Proparse to successfully parse a program, that program must be compilable. If it is not compilable, then there's no way to build a sensible and complete syntax tree. (There are ways to work with non-compilable files, but I won't get into that here.) Now let's ask the parser for a node handle. Proparse will create a node handle, and we will refer to that handle with an integer handle number. DEFINE VARIABLE myHandle AS INTEGER NO-UNDO. myHandle = parserGetHandle(). Pretty straightforward, I think. Proparse creates a handle, and returns the handle number, so that we have something to refer to it by. Now let's do something with that handle. We'll ask Proparse to point that handle at the very topmost node in the syntax tree that it built when it parsed "hello.p". parserNodeTop(myHandle). Let's have a look at a trivial output from the parser for the following program: run test. procedure test: disp "hello world". end procedure. And here's the output from the parser, which gives an idea of the shape of the tree. Each of the following lines represents a node in the tree:
Program_root
RUN run
FILENAME test
PERIOD .
PROCEDURE procedure
ID test
LEXCOLON :
Code_block
DISPLAY disp
Form_item
QSTRING "hello world"
PERIOD .
END end
PROCEDURE procedure
PERIOD .
Program_tail
Now we're ready to start examining the syntax tree. Let's ask Proparse to create a query for all DISPLAY nodes within the tree: DEFINE VARIABLE numResults AS INTEGER NO-UNDO. numResults = parserQueryCreate(myHandle, "myQuery":U, "DISPLAY":U). The first parameter is the handle where we want the query to start. Sometimes we want the query to start at the very top of the entire tree, sometimes (quite often, actually) we only want the query to start at the top of some branch - maybe a block branch or a statement branch. Because we may be working with many queries at once, we give them names so that we can refer to them later. That's the second parameter. Finally, for the third parameter, we tell it what type of nodes we're looking for. The query returns the number of results that it found. Now let's use the query:
DEFINE VARIABLE count AS INTEGER NO-UNDO.
REPEAT count = 1 TO numResults:
parserQueryGetResult("myQuery":U, count, myHandle).
DISPLAY
parserGetNodeType(myHandle)
parserGetNodeText(myHandle)
.
END.
For each result in the query, we ask Proparse to change our handle so that
it points to the same node in the syntax tree as the one found by the query.
Next, we display the node type, which will be "DISPLAY" which is not surprising
because that's what we asked for in our query.
But, in my program "hello.p", I had the following line:
We've scratched the surface of Proparse. There are many other node attributes, other ways to navigate the syntax tree, and other features of Proparse in general. However, these basics should be enough for you to start reading and making sense of Prolint rules. |