You don't get infinite recursion with right recursive rules if the prediction sets for the rules are different.
In the example T -> E + T | t
, if the first set of E
does not contain t
(I am presuming t
is a terminal), you are fine. The parsing procedure will be:
def T
if look_ahead is in first(E)
E
match ('+')
T
else if look_ahead == t
advance_lookahead
else syntax_error
With this, the recursion will stop when the look ahead contains t
.
With left recursion T -> T + E | t
, you are in trouble because first(T) always contains t
.
Conversion of left- to right recursion solves one problem but creates another. Right-recursive rules have the disadvantage that parsing lists requires stack space proportional to list length. Ideally you'd like the space a parser takes to be limited by a constant. So what to do?
The practical way to prevent many cases of right-recursion is to extend grammars to allow for iteration. In your case
T -> E + T | t
would become
T -> E + { E + } t
The curly braces denote "0 or more".
When the parser is implemented, the curly braces would become a while loop that uses the look ahead to decide whether to parse another instance of what's in the braces. With your example, you'd need first(E) not to contain terminal t.
A more common example is the standard expression grammar:
Expr -> Term { '+' Term }
Term -> Factor { '*' Factor }
Factor -> '0' | '(' Expr ')'
In this case, the recursive descent procedure for Expr
would be
def Expr
Term
while look_ahead == '+'
advance_lookahead
Term
If we didn't have the curly braces, the grammar would have to be:
Expr -> Term ExprTail
ExprTail -> '+' Term ExprTail | \eps
Term -> Factor TermTail
TermTail -> '*' Factor TermTail | \eps
Factor -> '0' | '(' Expr ')'
To parse Expr
s we now need two procedures:
def Expr
Term
ExprTail
def ExprTail
if look_ahead == '+'
advance_lookahead
Term
ExprTail
Interestingly, if you use a good compiler to compile both versions with optimizations turned on, it will detect tail recursion in ExprTail
, convert it into a loop, and then in-line the loop in Expr
. The resulting compiled codes will be exactly the same.