CS 3723/3721 Programming Languages
Recitation 5: Recursive Descent Parser


Overview: A recursive descent parser is a top-down parser, so called because it effectively builds a parse tree from the top (the start symbol) down, and from left to right. (The actual tree is not constructed but is implicit in a sequence of function calls.) This type of parser was very popular for real compilers in the past, but is not as popular now. The parser is usually written entirely by hand and does not require any sophisticated tools. It is a simple and effective technique, but is not as powerful as the fancy LR parsers.

This parser uses a recursive function corresponding to each grammar rule (that is, corresponding to each non-terminal symbol in the language). For simplicity one can just use the non-termianl as the name of the function. The body of each recursive function mirrors the right side of the corresponding rule. One decides which function to call based on the next input symbol.

Perhaps the hardest part of a recursive descent parser is the scanning: repeatedly fetching the next token from the scanner. It is tricky to decide when to scan, and the parser doesn't work at all if there is an extra scan or a missing scan. In the examples here and in the next recitation, all tokens are just single characters, so the scanner is simple, but it is still hard to do the scanning at the right places in the program.

Initial Example:

Initial Recitation work: Consider the following expanded version of this language:

Here "lower-case" stands for a single lower-case letter, and "upper-case" stands for a single upper-case letter.

Just to help with understanding, here is the intuitive meaning of each of the above non-terminals:

SymbolMeaning
MMain Program
LList of Statements
SStatement
IIf-Then-[Else] Statement
WWhile Statement
AAssignment Statement
PPut or Print (integer)
CPrint Character
GGet (integer)
EExpression (logical or arith)
TTerm
U(Hmmm. Ugly Term?)
FFactor

Also, the [ ] used for an if-then-else is often used in BNF for an optional item, while the { } used for a while is used above in the BNF for zero or more repetitions.

You are to write a recursive descent parser for this expanded language. Notice that this includes much of the earlier grammar as a special case, so that you can just use the earlier parser for these parts. To keep things simple, all the tokens are single characters, so that the scanner (lexical analyser) can be very simple, as shown in the earlier examples.

A true parser will input a sentence and either say that the sentence was legal or not. However, the parser is actually carrying out a complete traversal of the parse tree by the function calls and returns (as will be illustrated in class). The examples above have extra temporary output illustrating the calls and returns, so you can have confidence that your parser is working correctly. You should have temporary output also, though it does not have to be the same as the above examples.

What to turn in to the recitation instructor: Turn in a working version of your parser. You should start with very simple input as you are developing your parser -- perhaps just a single assignment statement at first, and then other single statements. Here is slightly more complicated sample source to use as the input to your parser:

To understand what this program does, you need to know the semantics of the P (Put an integer using <) and C (Put a character using <) constructs. < prints the value of the expression following it as an integer, without any extra blanks or newlines. < also prints a special character, chosen depending on the upper-case letter following it: B for a blank, T for a tab, N for a newline, etc.

The above program prints the integers from 0 to 39, followed on the same line by the corresponding Fibonacci number, followed by a 1 if the Fibonacci number is odd, and a 2 if it is even.

Here is a much more complicated sample source to use as the input to your parser:

This program produces the first m Fibonacci numbers along with their prime factorizations, where a value for m is read from the terminal using the > construct. Here is one possible result of running your parser with the above input: Parser output.

Key ideas: A recursive descent parser is particularly easy to write by hand and requires no special software tools as the other parsers do. It is easy to "cheat" with this parser, doing whatever you like. The capability of "cheating" can also lead to poorer code, particularly for a compiler that has to evolve over time.


Revision date: 2002-07-29. (Use ISO 8601, an International Standard.)