I think the problem here is not the indirect recursion but rather the ambiguity.
If it were just indirect recursion, you could simply substitute the right-hand sides of N
, A
and O
, eliminating the indirect recursion:
E → n E
| E a E
| E o E
| t
In order to get rid of the direct left recursion, you can left-factor:
E → n E
| E A'
| t
A'→ a E
| o E
and then remove the remaining left-recursion:
E → n E E'
| t E'
E'→ ε
| A' E'
A'→ a E
| o E
That has no left-recursion, direct or indirect, and every rule starts with a unique terminal. However, it's not LL(1), because there's a first/follow conflict.
That's really not surprising, because the original grammar was highly ambiguous, and left-recursion elimination doesn't eliminate ambiguity. The original grammar really only makes sense if accompanied by an operator precedence table.
That table indicates that AND
and OR
are left-associative operators with the same precedence (a slightly unusual decision), while NOT
is a unary operator with higher precedence. That, in turn, means that the BNF should have been written something like this:
N → n N
| t
E → A
| O
| N
A → E a N
O → E o N
N → n N
| t
The only difference from the grammar in the OP is the removal of ambiguity, as indicated by the precedence table.
Again, the first step is to substitute non-terminals A
and O
in order to make the left-recursion direct:
E → E a N
| E o N
| N
N → n N
| t
This is essentially the same grammar as the grammar for arithmetic expressions (ignoring multiplication, since there's only one precedence level), and the left-recursion can be eliminated directly:
E → N E'
E' → a E
| o E
| ε
N → n N
| t