CS 5363 Programming Languages and Compilers
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:

The Grammar: Consider the following expanded version of this language:

For a more colorful grammar that might be easier to understand, see here.

The terminal "letter" stands for a single lower-case letter, and "digit" stands for a single digit.

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

SymbolMeaning
MMain Program
SStatement
DFunction Definition
IIf-Then-[Else] Statement
WWhile Statement
AAssignment Statement
PPut or Print (integer)
CPrint Character
GGet (integer)
EExpression (logical or arith)
QRelational Expression (without | or &)
RArithmetic Expression (without relational ops)
TTerm (without + or -)
UUgly Term (without * or / or % either)
FFactor (parethsized, with unary + or -)

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 language. To keep things simple, you can initially make all the tokens single characters, so that the scanner (lexical analyser) can be very simple, as shown in the earlier examples. Later you can add identifiers that are more than one letter, and integer constants more than one digit.

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: 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.

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-10-07. (Use ISO 8601, an International Standard.)