Using Parse Constraints to Check for Reserved Words
When writing grammars for programming languages, we often need to distinguish
between "reserved words" and "identifiers". For example, in C++, a variable
cannot be named "for", since this name is reserved by the language. Many
reserved words (including "for") are used to introduce programming constructs,
so their correct interpretation is essential for successfully parsing the source
code. Hence, when scanning an identifier, the parser should reject any token
that matches a reserved word. This can be accomplished as follows:
cpp_ident ::=
identifier (? #VALUE !::= reserved_word; );
// partial list of reserved words
reserved_word ::=
"for"
| "while"
| "do" ;
/* etc. */
Here, we define the symbol "cpp_ident" in terms of the predefined identifier type, then apply a parse constraint that fails whenever the value of the identifier matches a reserved word. In this way, we effectively create a custom identifier type ("cpp_ident") that is specific to C++ and excludes language-defined reserved words.
For more information about using parse constraints, refer to pages 39 to 46 in the ProGrammar GDL Reference Guide.
Getting the Line Number for a Node in the Parse Tree
Using the ProGrammar API, the line number associated with any node in the parse tree can be determined as follows:
// C++ API example
long startPos = pParser->GetValuePos (nodeID);
long lineNum = pParser->GetInputLineNumber (startPos);
Yielding to the Client Application during the Parse
You can use breakpoints to force the parser to periodically return control to your application, allowing it to perform other tasks while the parse is in progress, such as refreshing the GUI. When the parser is called again, it resumes parsing at the point where it left off. The following code shows how to yield to the client application using a pgEventStepInc breakpoint.
// C++ API example
// The following breakpoint causes the parser to "break" every
// 1000 steps, thereby returning control to the caller.
pParser->SetBreakpoint(pgEventStepInc, 1000);
PGStatus parseStatus = pParser->Parse();
// The Parse method can return for two reasons:
//
// (1) because the parser has finished parsing, in which case
// parseStatus will be 'pgStatusComplete', or
//
// (2) because the parser encountered the breakpoint, in which
// case parseStatus will be 'pgStatusBreak'
while (parseStatus == pgStatusBreak)
{
// Now, the application can perform other tasks while the
// parser is paused.
// Keep parsing until all of the breakpoints have
// been processed. At any time, the parse can be
// aborted simply by breaking out of this loop and
// not calling Parse again...
parseStatus = pParser->Parse();
}
if (parseStatus == pgStatusComplete)
{
// finished parsing - process the results...
}
// end of example code
Note that this approach is often easier than creating a multi-threaded application, especially when the application would not otherwise need to be threaded.